mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-25 20:23:36 -04:00
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>
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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 @@ static int esw_offloads_metadata_init(struct mlx5_eswitch *esw)
|
||||
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 @@ int mlx5_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode,
|
||||
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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
if (mlx5_get_sd(dev))
|
||||
return mlx5_lag_demux_ft_fg_init(dev, ft_attr, ldev);
|
||||
xa_init(&pf->lag_demux_rules);
|
||||
|
||||
return mlx5_lag_demux_fw_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, 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)
|
||||
mlx5_del_flow_rules(rule);
|
||||
xa_destroy(&ldev->lag_demux_rules);
|
||||
pf = mlx5_lag_pf_by_dev(ldev, dev);
|
||||
if (!pf)
|
||||
return;
|
||||
|
||||
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;
|
||||
xa_for_each(&pf->lag_demux_rules, vport_num, rule)
|
||||
mlx5_del_flow_rules(rule);
|
||||
xa_destroy(&pf->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;
|
||||
}
|
||||
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;
|
||||
|
||||
if (xa_load(&ldev->lag_demux_rules, index))
|
||||
master = mlx5_lag_dev_get_master_pf(ldev, vport_dev);
|
||||
if (!master || !master->lag_demux_fg)
|
||||
return 0;
|
||||
|
||||
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;
|
||||
|
||||
rule = xa_erase(&ldev->lag_demux_rules, index);
|
||||
master_pf = mlx5_lag_dev_get_master_pf(ldev, dev);
|
||||
if (!master_pf || !master_pf->lag_demux_fg)
|
||||
return;
|
||||
|
||||
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);
|
||||
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);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&ldev->lock);
|
||||
mlx5_devcom_comp_unlock(sd_devcom);
|
||||
}
|
||||
|
||||
void mlx5_lag_enable_change(struct mlx5_core_dev *dev)
|
||||
|
||||
@@ -59,6 +59,10 @@ struct lag_func {
|
||||
struct mlx5_nb port_change_nb;
|
||||
u32 group_id; /* SD group ID, 0 = not SD */
|
||||
bool sd_fdb_active; /* set on all SD group members */
|
||||
/* Lag demux resources - only populated on master devices */
|
||||
struct mlx5_flow_table *lag_demux_ft;
|
||||
struct mlx5_flow_group *lag_demux_fg;
|
||||
struct xarray lag_demux_rules;
|
||||
};
|
||||
|
||||
/* Used for collection of netdev event info. */
|
||||
@@ -95,9 +99,6 @@ struct mlx5_lag {
|
||||
/* Protect lag fields/state changes */
|
||||
struct mutex lock;
|
||||
struct lag_mpesw lag_mpesw;
|
||||
struct mlx5_flow_table *lag_demux_ft;
|
||||
struct mlx5_flow_group *lag_demux_fg;
|
||||
struct xarray lag_demux_rules;
|
||||
};
|
||||
|
||||
static inline struct mlx5_lag *
|
||||
@@ -156,6 +157,14 @@ __mlx5_lag_is_sd(struct mlx5_lag *ldev, struct mlx5_core_dev *dev)
|
||||
return pf && pf->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);
|
||||
|
||||
return pf && xa_get_mark(&ldev->pfs, pf->idx, MLX5_LAG_XA_MARK_PORT);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
__mlx5_lag_is_active(struct mlx5_lag *ldev)
|
||||
{
|
||||
@@ -174,6 +183,8 @@ int mlx5_lag_shared_fdb_create(struct mlx5_lag *ldev,
|
||||
enum mlx5_lag_mode mode,
|
||||
u32 group_id);
|
||||
void mlx5_lag_shared_fdb_destroy(struct mlx5_lag *ldev, u32 group_id);
|
||||
int mlx5_lag_create_vport_lag(struct mlx5_lag *ldev, u32 group_id);
|
||||
int mlx5_lag_destroy_vport_lag(struct mlx5_lag *ldev, u32 group_id);
|
||||
int mlx5_lag_create_single_fdb(struct mlx5_lag *ldev);
|
||||
void mlx5_lag_destroy_single_fdb(struct mlx5_lag *ldev);
|
||||
bool mlx5_lag_shared_fdb_supported(struct mlx5_lag *ldev);
|
||||
@@ -190,6 +201,18 @@ static inline int mlx5_lag_shared_fdb_create(struct mlx5_lag *ldev,
|
||||
static inline void mlx5_lag_shared_fdb_destroy(struct mlx5_lag *ldev,
|
||||
u32 group_id) {}
|
||||
|
||||
static inline int mlx5_lag_create_vport_lag(struct mlx5_lag *ldev,
|
||||
u32 group_id)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int mlx5_lag_destroy_vport_lag(struct mlx5_lag *ldev,
|
||||
u32 group_id)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int mlx5_lag_create_single_fdb(struct mlx5_lag *ldev)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
@@ -287,6 +310,7 @@ int mlx5_lag_num_devs(struct mlx5_lag *ldev);
|
||||
int mlx5_lag_num_netdevs(struct mlx5_lag *ldev);
|
||||
int mlx5_lag_reload_ib_reps_from_locked(struct mlx5_lag *ldev, u32 flags,
|
||||
u32 filter, bool cont_on_fail);
|
||||
void mlx5_lag_unload_reps_from_locked(struct mlx5_lag *ldev, u32 filter);
|
||||
int mlx5_ldev_add_mdev(struct mlx5_lag *ldev, struct mlx5_core_dev *dev,
|
||||
u32 group_id);
|
||||
void mlx5_ldev_remove_mdev(struct mlx5_lag *ldev, struct mlx5_core_dev *dev);
|
||||
|
||||
@@ -15,7 +15,7 @@ static void mlx5_mpesw_metadata_cleanup(struct mlx5_lag *ldev)
|
||||
u32 pf_metadata;
|
||||
int i;
|
||||
|
||||
mlx5_ldev_for_each(i, 0, ldev) {
|
||||
mlx5_lag_for_each(i, 0, ldev, MLX5_LAG_FILTER_ALL) {
|
||||
dev = mlx5_lag_pf(ldev, i)->dev;
|
||||
esw = dev->priv.eswitch;
|
||||
pf_metadata = ldev->lag_mpesw.pf_metadata[i];
|
||||
@@ -36,7 +36,7 @@ static int mlx5_mpesw_metadata_set(struct mlx5_lag *ldev)
|
||||
u32 pf_metadata;
|
||||
int i, err;
|
||||
|
||||
mlx5_ldev_for_each(i, 0, ldev) {
|
||||
mlx5_lag_for_each(i, 0, ldev, MLX5_LAG_FILTER_ALL) {
|
||||
dev = mlx5_lag_pf(ldev, i)->dev;
|
||||
esw = dev->priv.eswitch;
|
||||
pf_metadata = mlx5_esw_match_metadata_alloc(esw);
|
||||
@@ -52,7 +52,7 @@ static int mlx5_mpesw_metadata_set(struct mlx5_lag *ldev)
|
||||
goto err_metadata;
|
||||
}
|
||||
|
||||
mlx5_ldev_for_each(i, 0, ldev) {
|
||||
mlx5_lag_for_each(i, 0, ldev, MLX5_LAG_FILTER_ALL) {
|
||||
dev = mlx5_lag_pf(ldev, i)->dev;
|
||||
mlx5_notifier_call_chain(dev->priv.events, MLX5_DEV_EVENT_MULTIPORT_ESW,
|
||||
(void *)0);
|
||||
@@ -65,6 +65,48 @@ static int mlx5_mpesw_metadata_set(struct mlx5_lag *ldev)
|
||||
return err;
|
||||
}
|
||||
|
||||
static void mlx5_mpesw_restore_sd_fdb(struct mlx5_lag *ldev)
|
||||
{
|
||||
struct lag_func *pf;
|
||||
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);
|
||||
if (err)
|
||||
mlx5_core_warn(pf->dev,
|
||||
"Failed to restore SD shared FDB (%d)\n",
|
||||
err);
|
||||
}
|
||||
}
|
||||
|
||||
static int mlx5_mpesw_teardown_sd_fdb(struct mlx5_lag *ldev)
|
||||
{
|
||||
struct lag_func *pf;
|
||||
int i;
|
||||
|
||||
mlx5_ldev_for_each(i, 0, ldev) {
|
||||
pf = mlx5_lag_pf(ldev, i);
|
||||
if (!pf->sd_fdb_active)
|
||||
continue;
|
||||
mlx5_lag_shared_fdb_destroy(ldev, pf->group_id);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool mlx5_lag_has_sd_group(struct mlx5_lag *ldev)
|
||||
{
|
||||
struct lag_func *pf;
|
||||
int i;
|
||||
|
||||
mlx5_ldev_for_each(i, 0, ldev) {
|
||||
pf = mlx5_lag_pf(ldev, i);
|
||||
if (pf->group_id)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int mlx5_lag_enable_mpesw(struct mlx5_lag *ldev)
|
||||
{
|
||||
int idx = mlx5_lag_get_dev_index_by_seq(ldev, MLX5_LAG_P1);
|
||||
@@ -92,10 +134,17 @@ static int mlx5_lag_enable_mpesw(struct mlx5_lag *ldev)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (mlx5_lag_has_sd_group(ldev))
|
||||
mlx5_mpesw_teardown_sd_fdb(ldev);
|
||||
|
||||
err = mlx5_lag_shared_fdb_create(ldev, NULL, MLX5_LAG_MODE_MPESW,
|
||||
MLX5_LAG_FILTER_ALL);
|
||||
if (err) {
|
||||
mlx5_core_warn(dev0, "Failed to create LAG in MPESW mode (%d)\n", err);
|
||||
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;
|
||||
}
|
||||
@@ -105,9 +154,36 @@ static int mlx5_lag_enable_mpesw(struct mlx5_lag *ldev)
|
||||
|
||||
void mlx5_lag_disable_mpesw(struct mlx5_lag *ldev)
|
||||
{
|
||||
if (ldev->mode == MLX5_LAG_MODE_MPESW) {
|
||||
mlx5_mpesw_metadata_cleanup(ldev);
|
||||
mlx5_lag_shared_fdb_destroy(ldev, MLX5_LAG_FILTER_ALL);
|
||||
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);
|
||||
}
|
||||
|
||||
void mlx5_mpesw_sd_devcoms_lock(struct mlx5_lag *ldev)
|
||||
{
|
||||
struct mlx5_devcom_comp_dev *sd_devcom;
|
||||
int i;
|
||||
|
||||
mlx5_ldev_for_each(i, 0, ldev) {
|
||||
sd_devcom = mlx5_sd_get_devcom(mlx5_lag_pf(ldev, i)->dev);
|
||||
if (sd_devcom)
|
||||
mlx5_devcom_comp_lock(sd_devcom);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
if (sd_devcom)
|
||||
mlx5_devcom_comp_unlock(sd_devcom);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,6 +198,7 @@ static void mlx5_mpesw_work(struct work_struct *work)
|
||||
return;
|
||||
|
||||
mlx5_devcom_comp_lock(devcom);
|
||||
mlx5_mpesw_sd_devcoms_lock(ldev);
|
||||
mutex_lock(&ldev->lock);
|
||||
if (ldev->mode_changes_in_progress) {
|
||||
mpesww->result = -EAGAIN;
|
||||
@@ -134,6 +211,7 @@ static void mlx5_mpesw_work(struct work_struct *work)
|
||||
mlx5_lag_disable_mpesw(ldev);
|
||||
unlock:
|
||||
mutex_unlock(&ldev->lock);
|
||||
mlx5_mpesw_sd_devcoms_unlock(ldev);
|
||||
mlx5_devcom_comp_unlock(devcom);
|
||||
complete(&mpesww->comp);
|
||||
}
|
||||
@@ -199,7 +277,8 @@ 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;
|
||||
return ldev && ldev->mode == MLX5_LAG_MODE_MPESW &&
|
||||
__mlx5_lag_dev_is_port(ldev, dev);
|
||||
}
|
||||
EXPORT_SYMBOL(mlx5_lag_is_mpesw);
|
||||
|
||||
|
||||
@@ -33,8 +33,12 @@ void mlx5_lag_mpesw_disable(struct mlx5_core_dev *dev);
|
||||
int mlx5_lag_mpesw_enable(struct mlx5_core_dev *dev);
|
||||
#ifdef CONFIG_MLX5_ESWITCH
|
||||
void mlx5_lag_disable_mpesw(struct mlx5_lag *ldev);
|
||||
void mlx5_mpesw_sd_devcoms_lock(struct mlx5_lag *ldev);
|
||||
void mlx5_mpesw_sd_devcoms_unlock(struct mlx5_lag *ldev);
|
||||
#else
|
||||
static inline void mlx5_lag_disable_mpesw(struct mlx5_lag *ldev) {}
|
||||
static inline void mlx5_mpesw_sd_devcoms_lock(struct mlx5_lag *ldev) {}
|
||||
static inline void mlx5_mpesw_sd_devcoms_unlock(struct mlx5_lag *ldev) {}
|
||||
#endif /* CONFIG_MLX5_ESWITCH */
|
||||
|
||||
#ifdef CONFIG_MLX5_ESWITCH
|
||||
|
||||
@@ -89,6 +89,76 @@ static int mlx5_lag_create_single_fdb_filter(struct mlx5_lag *ldev, u32 filter)
|
||||
return err;
|
||||
}
|
||||
|
||||
int mlx5_lag_create_vport_lag(struct mlx5_lag *ldev, u32 group_id)
|
||||
{
|
||||
u32 filter = group_id ? group_id : MLX5_LAG_FILTER_ALL;
|
||||
int master_idx = mlx5_lag_get_dev_index_by_seq_filter(ldev, MLX5_LAG_P1,
|
||||
filter);
|
||||
struct mlx5_eswitch *master_esw;
|
||||
struct mlx5_core_dev *dev0;
|
||||
int i, j;
|
||||
int err;
|
||||
|
||||
if (master_idx < 0)
|
||||
return -EINVAL;
|
||||
|
||||
dev0 = mlx5_lag_pf(ldev, master_idx)->dev;
|
||||
master_esw = dev0->priv.eswitch;
|
||||
|
||||
mlx5_lag_for_each(i, 0, ldev, filter) {
|
||||
struct mlx5_eswitch *slave_esw;
|
||||
|
||||
if (i == master_idx)
|
||||
continue;
|
||||
|
||||
slave_esw = mlx5_lag_pf(ldev, i)->dev->priv.eswitch;
|
||||
err = mlx5_eswitch_offloads_vport_lag_add_one(master_esw,
|
||||
slave_esw);
|
||||
if (err)
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
mlx5_lag_for_each_reverse(j, i - 1, 0, ldev, filter) {
|
||||
struct mlx5_eswitch *slave_esw;
|
||||
|
||||
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);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int mlx5_lag_destroy_vport_lag(struct mlx5_lag *ldev, u32 group_id)
|
||||
{
|
||||
u32 filter = group_id ? group_id : MLX5_LAG_FILTER_ALL;
|
||||
int master_idx = mlx5_lag_get_dev_index_by_seq_filter(ldev, MLX5_LAG_P1,
|
||||
filter);
|
||||
struct mlx5_eswitch *master_esw;
|
||||
struct mlx5_core_dev *dev0;
|
||||
int i;
|
||||
|
||||
if (master_idx < 0)
|
||||
return 0;
|
||||
|
||||
dev0 = mlx5_lag_pf(ldev, master_idx)->dev;
|
||||
master_esw = dev0->priv.eswitch;
|
||||
|
||||
mlx5_lag_for_each(i, 0, ldev, filter) {
|
||||
struct mlx5_core_dev *dev;
|
||||
|
||||
if (i == master_idx)
|
||||
continue;
|
||||
dev = mlx5_lag_pf(ldev, i)->dev;
|
||||
mlx5_eswitch_offloads_vport_lag_del_one(master_esw,
|
||||
dev->priv.eswitch);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mlx5_lag_destroy_single_fdb_filter(struct mlx5_lag *ldev,
|
||||
u32 filter)
|
||||
{
|
||||
@@ -141,7 +211,7 @@ int mlx5_lag_shared_fdb_create(struct mlx5_lag *ldev,
|
||||
enum mlx5_lag_mode mode,
|
||||
u32 group_id)
|
||||
{
|
||||
u32 filter = group_id ? group_id : MLX5_LAG_FILTER_PORTS;
|
||||
u32 filter = group_id ? group_id : MLX5_LAG_FILTER_ALL;
|
||||
int idx = mlx5_lag_get_dev_index_by_seq_filter(ldev, MLX5_LAG_P1,
|
||||
filter);
|
||||
struct mlx5_core_dev *dev0;
|
||||
@@ -209,7 +279,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_PORTS;
|
||||
u32 filter = group_id ? group_id : MLX5_LAG_FILTER_ALL;
|
||||
struct lag_func *pf;
|
||||
int err;
|
||||
int i;
|
||||
@@ -226,6 +296,7 @@ void mlx5_lag_shared_fdb_destroy(struct mlx5_lag *ldev, u32 group_id)
|
||||
pf->sd_fdb_active = false;
|
||||
}
|
||||
mlx5_lag_destroy_single_fdb_filter(ldev, group_id);
|
||||
mlx5_lag_unload_reps_from_locked(ldev, filter);
|
||||
}
|
||||
|
||||
mlx5_lag_add_devices_filter(ldev, filter);
|
||||
|
||||
@@ -287,9 +287,9 @@ int mlx5_devcom_comp_get_size(struct mlx5_devcom_comp_dev *devcom)
|
||||
return kref_read(&comp->ref);
|
||||
}
|
||||
|
||||
int mlx5_devcom_send_event(struct mlx5_devcom_comp_dev *devcom,
|
||||
int event, int rollback_event,
|
||||
void *event_data)
|
||||
int mlx5_devcom_locked_send_event(struct mlx5_devcom_comp_dev *devcom,
|
||||
int event, int rollback_event,
|
||||
void *event_data)
|
||||
{
|
||||
struct mlx5_devcom_comp_dev *pos;
|
||||
struct mlx5_devcom_comp *comp;
|
||||
@@ -299,24 +299,28 @@ int mlx5_devcom_send_event(struct mlx5_devcom_comp_dev *devcom,
|
||||
if (!devcom)
|
||||
return -ENODEV;
|
||||
|
||||
lockdep_assert_held_write(&devcom->comp->sem);
|
||||
comp = devcom->comp;
|
||||
down_write(&comp->sem);
|
||||
list_for_each_entry(pos, &comp->comp_dev_list_head, list) {
|
||||
data = rcu_dereference_protected(pos->data, lockdep_is_held(&comp->sem));
|
||||
|
||||
if (pos != devcom && data) {
|
||||
err = comp->handler(event, data, event_data);
|
||||
if (err)
|
||||
if (err && rollback_event != DEVCOM_CANT_FAIL) {
|
||||
goto rollback;
|
||||
} else if (err && rollback_event == DEVCOM_CANT_FAIL) {
|
||||
WARN_ONCE(1, "devcom component %d event %d failed: %d\n",
|
||||
comp->id, event, err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
up_write(&comp->sem);
|
||||
return 0;
|
||||
|
||||
rollback:
|
||||
if (list_entry_is_head(pos, &comp->comp_dev_list_head, list))
|
||||
goto out;
|
||||
return err;
|
||||
pos = list_prev_entry(pos, list);
|
||||
list_for_each_entry_from_reverse(pos, &comp->comp_dev_list_head, list) {
|
||||
data = rcu_dereference_protected(pos->data, lockdep_is_held(&comp->sem));
|
||||
@@ -324,7 +328,23 @@ int mlx5_devcom_send_event(struct mlx5_devcom_comp_dev *devcom,
|
||||
if (pos != devcom && data)
|
||||
comp->handler(rollback_event, data, event_data);
|
||||
}
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
int mlx5_devcom_send_event(struct mlx5_devcom_comp_dev *devcom,
|
||||
int event, int rollback_event,
|
||||
void *event_data)
|
||||
{
|
||||
struct mlx5_devcom_comp *comp;
|
||||
int err;
|
||||
|
||||
if (!devcom)
|
||||
return -ENODEV;
|
||||
|
||||
comp = devcom->comp;
|
||||
down_write(&comp->sem);
|
||||
err = mlx5_devcom_locked_send_event(devcom, event, rollback_event,
|
||||
event_data);
|
||||
up_write(&comp->sem);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -46,6 +46,11 @@ mlx5_devcom_register_component(struct mlx5_devcom_dev *devc,
|
||||
void *data);
|
||||
void mlx5_devcom_unregister_component(struct mlx5_devcom_comp_dev *devcom);
|
||||
|
||||
#define DEVCOM_CANT_FAIL (INT_MAX)
|
||||
|
||||
int mlx5_devcom_locked_send_event(struct mlx5_devcom_comp_dev *devcom,
|
||||
int event, int rollback_event,
|
||||
void *event_data);
|
||||
int mlx5_devcom_send_event(struct mlx5_devcom_comp_dev *devcom,
|
||||
int event, int rollback_event,
|
||||
void *event_data);
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "../lag/lag.h"
|
||||
#include "mlx5_core.h"
|
||||
#include "lib/mlx5.h"
|
||||
#include "devlink.h"
|
||||
#include "eswitch.h"
|
||||
#include "fs_cmd.h"
|
||||
#include <linux/mlx5/eswitch.h>
|
||||
#include <linux/mlx5/vport.h>
|
||||
@@ -22,14 +24,19 @@ struct mlx5_sd {
|
||||
struct dentry *dfs;
|
||||
u8 state;
|
||||
bool primary;
|
||||
bool fw_silents_secondaries;
|
||||
union {
|
||||
struct { /* primary */
|
||||
struct mlx5_core_dev *secondaries[MLX5_SD_MAX_GROUP_SZ - 1];
|
||||
struct mlx5_flow_table *tx_ft;
|
||||
/* Next index for secondary registration */
|
||||
u8 next_secondary_idx;
|
||||
};
|
||||
struct { /* secondary */
|
||||
struct mlx5_core_dev *primary_dev;
|
||||
u32 alias_obj_id;
|
||||
/* TX flow table root in switchdev (silent) config */
|
||||
bool tx_root_silent;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -82,6 +89,27 @@ bool mlx5_sd_is_primary(struct mlx5_core_dev *dev)
|
||||
return sd->primary;
|
||||
}
|
||||
|
||||
int mlx5_sd_pf_num_get(struct mlx5_core_dev *dev)
|
||||
{
|
||||
struct mlx5_sd *sd = mlx5_get_sd(dev);
|
||||
int pf_num = mlx5_get_dev_index(dev);
|
||||
struct mlx5_core_dev *pos;
|
||||
int i;
|
||||
|
||||
if (!sd)
|
||||
return pf_num;
|
||||
|
||||
mlx5_devcom_comp_assert_locked(sd->devcom);
|
||||
if (!mlx5_devcom_comp_is_ready(sd->devcom))
|
||||
return -ENODEV;
|
||||
|
||||
mlx5_sd_for_each_dev(i, mlx5_sd_get_primary(dev), pos)
|
||||
if (pos == dev)
|
||||
break;
|
||||
|
||||
return pf_num * sd->host_buses + i;
|
||||
}
|
||||
|
||||
struct mlx5_core_dev *
|
||||
mlx5_sd_primary_get_peer(struct mlx5_core_dev *primary, int idx)
|
||||
{
|
||||
@@ -165,7 +193,8 @@ static bool mlx5_sd_caps_supported(struct mlx5_core_dev *dev, u8 host_buses)
|
||||
/* Disconnect secondaries from the network */
|
||||
if (!MLX5_CAP_GEN(dev, eswitch_manager))
|
||||
return false;
|
||||
if (!MLX5_CAP_GEN(dev, silent_mode_set))
|
||||
if (!MLX5_CAP_GEN(dev, silent_mode_set) &&
|
||||
!MLX5_CAP_GEN(dev, silent_mode_query))
|
||||
return false;
|
||||
|
||||
/* RX steering from primary to secondaries */
|
||||
@@ -193,10 +222,6 @@ bool mlx5_sd_is_supported(struct mlx5_core_dev *dev)
|
||||
if (!mlx5_core_is_pf(dev))
|
||||
return false;
|
||||
|
||||
/* Block on embedded CPU PFs */
|
||||
if (mlx5_core_is_ecpf(dev))
|
||||
return false;
|
||||
|
||||
err = mlx5_query_nic_vport_sd_group(dev, &sd_group);
|
||||
if (err || !sd_group)
|
||||
return false;
|
||||
@@ -223,10 +248,6 @@ static int sd_init(struct mlx5_core_dev *dev)
|
||||
if (!mlx5_core_is_pf(dev))
|
||||
return 0;
|
||||
|
||||
/* Block on embedded CPU PFs */
|
||||
if (mlx5_core_is_ecpf(dev))
|
||||
return 0;
|
||||
|
||||
err = mlx5_query_nic_vport_sd_group(dev, &sd_group);
|
||||
if (err)
|
||||
return err;
|
||||
@@ -374,62 +395,196 @@ static void sd_lag_cleanup(struct mlx5_core_dev *dev)
|
||||
mutex_unlock(&ldev->lock);
|
||||
}
|
||||
|
||||
enum {
|
||||
SD_PRIMARY_SET,
|
||||
SD_SECONDARIES_SET,
|
||||
SD_FW_SILENT_CHECK,
|
||||
};
|
||||
|
||||
static int sd_handle_fw_silent_check(struct mlx5_core_dev *dev,
|
||||
struct mlx5_core_dev *peer)
|
||||
{
|
||||
struct mlx5_sd *peer_sd = mlx5_get_sd(peer);
|
||||
struct mlx5_sd *sd = mlx5_get_sd(dev);
|
||||
u8 dev_silent = 0, peer_silent = 0;
|
||||
int err;
|
||||
|
||||
if (peer_sd->fw_silents_secondaries) {
|
||||
sd->fw_silents_secondaries = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = mlx5_fs_cmd_query_l2table_silent(dev, &dev_silent);
|
||||
if (err) {
|
||||
sd_warn(dev, "Failed to query silent mode for dev: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = mlx5_fs_cmd_query_l2table_silent(peer, &peer_silent);
|
||||
if (err) {
|
||||
sd_warn(dev, "Failed to query silent mode for peer: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (dev_silent || peer_silent) {
|
||||
sd->fw_silents_secondaries = true;
|
||||
peer_sd->fw_silents_secondaries = true;
|
||||
sd_info(dev, "FW indicates at least one device is silent\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_handle_primary_set(struct mlx5_core_dev *dev,
|
||||
struct mlx5_core_dev *peer)
|
||||
{
|
||||
struct mlx5_sd *peer_sd = mlx5_get_sd(peer);
|
||||
struct mlx5_sd *sd = mlx5_get_sd(dev);
|
||||
struct mlx5_core_dev *candidate;
|
||||
struct mlx5_sd *candidate_sd;
|
||||
bool dev_should_be_primary;
|
||||
|
||||
/* Peer is the device that being sent to all the other devices in the
|
||||
* group. Hence, use peer to get the candidate device.
|
||||
*/
|
||||
candidate = peer_sd->primary ? peer : peer_sd->primary_dev;
|
||||
|
||||
if (sd->fw_silents_secondaries) {
|
||||
u8 candidate_silent = 0;
|
||||
int err;
|
||||
|
||||
err = mlx5_fs_cmd_query_l2table_silent(candidate,
|
||||
&candidate_silent);
|
||||
if (err) {
|
||||
sd_warn(candidate, "Failed to query silent mode for dev: %d\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
/* Candidate is silent, dev should be primary */
|
||||
dev_should_be_primary = candidate_silent;
|
||||
} else {
|
||||
/* No FW silent mode, use bus number */
|
||||
dev_should_be_primary =
|
||||
dev->pdev->bus->number < candidate->pdev->bus->number;
|
||||
}
|
||||
|
||||
if (!dev_should_be_primary)
|
||||
return 0;
|
||||
|
||||
candidate_sd = mlx5_get_sd(candidate);
|
||||
|
||||
sd->primary = true;
|
||||
candidate_sd->primary = false;
|
||||
candidate_sd->primary_dev = dev;
|
||||
peer_sd->primary = false;
|
||||
peer_sd->primary_dev = dev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sd_handle_secondaries_set(struct mlx5_core_dev *dev,
|
||||
struct mlx5_core_dev *peer)
|
||||
{
|
||||
struct mlx5_sd *peer_sd = mlx5_get_sd(peer);
|
||||
struct mlx5_sd *sd = mlx5_get_sd(dev);
|
||||
u8 idx;
|
||||
|
||||
/* Primary has nothing to register with itself. */
|
||||
if (sd->primary)
|
||||
return;
|
||||
|
||||
/* dev is a secondary device, peer is the primary device.
|
||||
* Secondary registers itself with the primary.
|
||||
*/
|
||||
idx = peer_sd->next_secondary_idx++;
|
||||
peer_sd->secondaries[idx] = dev;
|
||||
sd->primary_dev = peer;
|
||||
}
|
||||
|
||||
static int mlx5_sd_devcom_event(int event, void *my_data, void *event_data)
|
||||
{
|
||||
struct mlx5_core_dev *peer = event_data;
|
||||
struct mlx5_core_dev *dev = my_data;
|
||||
|
||||
switch (event) {
|
||||
case SD_FW_SILENT_CHECK:
|
||||
return sd_handle_fw_silent_check(dev, peer);
|
||||
case SD_PRIMARY_SET:
|
||||
return sd_handle_primary_set(dev, peer);
|
||||
case SD_SECONDARIES_SET:
|
||||
sd_handle_secondaries_set(dev, peer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_register(struct mlx5_core_dev *dev)
|
||||
{
|
||||
struct mlx5_devcom_comp_dev *devcom, *pos;
|
||||
struct mlx5_devcom_match_attr attr = {};
|
||||
struct mlx5_core_dev *peer, *primary;
|
||||
struct mlx5_sd *sd, *primary_sd;
|
||||
int err, i;
|
||||
struct mlx5_devcom_comp_dev *devcom;
|
||||
struct mlx5_core_dev *primary;
|
||||
struct mlx5_sd *primary_sd;
|
||||
struct mlx5_sd *sd;
|
||||
int err;
|
||||
|
||||
sd = mlx5_get_sd(dev);
|
||||
attr.key.val = sd->group_id;
|
||||
attr.flags = MLX5_DEVCOM_MATCH_FLAGS_NS;
|
||||
attr.net = mlx5_core_net(dev);
|
||||
devcom = mlx5_devcom_register_component(dev->priv.devc, MLX5_DEVCOM_SD_GROUP,
|
||||
&attr, NULL, dev);
|
||||
devcom = mlx5_devcom_register_component(dev->priv.devc,
|
||||
MLX5_DEVCOM_SD_GROUP,
|
||||
&attr, mlx5_sd_devcom_event,
|
||||
dev);
|
||||
if (!devcom)
|
||||
return -EINVAL;
|
||||
|
||||
sd->devcom = devcom;
|
||||
|
||||
if (mlx5_devcom_comp_get_size(devcom) != sd->host_buses)
|
||||
return 0;
|
||||
|
||||
mlx5_devcom_comp_lock(devcom);
|
||||
mlx5_devcom_comp_set_ready(devcom, true);
|
||||
mlx5_devcom_comp_unlock(devcom);
|
||||
if (mlx5_devcom_comp_get_size(devcom) != sd->host_buses ||
|
||||
mlx5_devcom_comp_is_ready(devcom))
|
||||
goto out;
|
||||
|
||||
if (!mlx5_devcom_for_each_peer_begin(devcom)) {
|
||||
err = -ENODEV;
|
||||
goto err_devcom_unreg;
|
||||
/* If silent mode query is supported, ask each device whether it is
|
||||
* silent and propagate the result to the whole group. In each group
|
||||
* only one device is not silent
|
||||
*/
|
||||
if (MLX5_CAP_GEN(dev, silent_mode_query)) {
|
||||
err = mlx5_devcom_locked_send_event(devcom, SD_FW_SILENT_CHECK,
|
||||
SD_FW_SILENT_CHECK, dev);
|
||||
if (err)
|
||||
goto err_devcom_unreg;
|
||||
}
|
||||
|
||||
primary = dev;
|
||||
mlx5_devcom_for_each_peer_entry(devcom, peer, pos)
|
||||
if (peer->pdev->bus->number < primary->pdev->bus->number)
|
||||
primary = peer;
|
||||
/* Send SD_PRIMARY_SET event with this device.
|
||||
* All peers will receive this event and compare to this device.
|
||||
* If fw_silents_secondaries is set, choose non-silent device.
|
||||
* Otherwise use bus number.
|
||||
*/
|
||||
sd->primary = true;
|
||||
err = mlx5_devcom_locked_send_event(devcom, SD_PRIMARY_SET,
|
||||
SD_PRIMARY_SET, dev);
|
||||
if (err)
|
||||
goto err_devcom_unreg;
|
||||
|
||||
/* Broadcast SD_SECONDARIES_SET. Each non-sender peer's handler runs;
|
||||
* the primary's handler returns early so only secondaries register.
|
||||
*/
|
||||
primary = sd->primary ? dev : sd->primary_dev;
|
||||
if (!sd->primary)
|
||||
sd_handle_secondaries_set(dev, primary);
|
||||
mlx5_devcom_locked_send_event(devcom, SD_SECONDARIES_SET,
|
||||
DEVCOM_CANT_FAIL, primary);
|
||||
|
||||
primary_sd = mlx5_get_sd(primary);
|
||||
primary_sd->primary = true;
|
||||
i = 0;
|
||||
/* loop the secondaries */
|
||||
mlx5_devcom_for_each_peer_entry(primary_sd->devcom, peer, pos) {
|
||||
struct mlx5_sd *peer_sd = mlx5_get_sd(peer);
|
||||
|
||||
primary_sd->secondaries[i++] = peer;
|
||||
peer_sd->primary = false;
|
||||
peer_sd->primary_dev = primary;
|
||||
}
|
||||
|
||||
mlx5_devcom_for_each_peer_end(devcom);
|
||||
if (primary_sd->next_secondary_idx + 1 == sd->host_buses)
|
||||
mlx5_devcom_comp_set_ready(devcom, true);
|
||||
out:
|
||||
mlx5_devcom_comp_unlock(devcom);
|
||||
return 0;
|
||||
|
||||
err_devcom_unreg:
|
||||
mlx5_devcom_comp_lock(sd->devcom);
|
||||
mlx5_devcom_comp_set_ready(sd->devcom, false);
|
||||
mlx5_devcom_comp_unlock(sd->devcom);
|
||||
mlx5_devcom_unregister_component(sd->devcom);
|
||||
mlx5_devcom_comp_unlock(devcom);
|
||||
mlx5_devcom_unregister_component(devcom);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -513,6 +668,29 @@ static void sd_secondary_destroy_alias_ft(struct mlx5_core_dev *secondary)
|
||||
MLX5_GENERAL_OBJECT_TYPES_FLOW_TABLE_ALIAS);
|
||||
}
|
||||
|
||||
static int mlx5_sd_secondary_conf_tx_root(struct mlx5_core_dev *secondary,
|
||||
bool disconnect)
|
||||
{
|
||||
struct mlx5_sd *sd = mlx5_get_sd(secondary);
|
||||
int err;
|
||||
|
||||
/* Idempotent: skip if TX root is already in the requested state. */
|
||||
if (sd->tx_root_silent == disconnect)
|
||||
return 0;
|
||||
|
||||
if (disconnect)
|
||||
err = mlx5_fs_cmd_set_tx_flow_table_root(secondary, 0, true);
|
||||
else
|
||||
err = mlx5_fs_cmd_set_tx_flow_table_root(secondary,
|
||||
sd->alias_obj_id,
|
||||
false);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
sd->tx_root_silent = disconnect;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_cmd_set_secondary(struct mlx5_core_dev *secondary,
|
||||
struct mlx5_core_dev *primary,
|
||||
u8 *alias_key)
|
||||
@@ -521,33 +699,42 @@ static int sd_cmd_set_secondary(struct mlx5_core_dev *secondary,
|
||||
struct mlx5_sd *sd = mlx5_get_sd(secondary);
|
||||
int err;
|
||||
|
||||
err = mlx5_fs_cmd_set_l2table_entry_silent(secondary, 1);
|
||||
if (err)
|
||||
return err;
|
||||
if (!primary_sd->fw_silents_secondaries) {
|
||||
err = mlx5_fs_cmd_set_l2table_entry_silent(secondary, 1);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = sd_secondary_create_alias_ft(secondary, primary, primary_sd->tx_ft,
|
||||
&sd->alias_obj_id, alias_key);
|
||||
if (err)
|
||||
goto err_unset_silent;
|
||||
|
||||
err = mlx5_fs_cmd_set_tx_flow_table_root(secondary, sd->alias_obj_id, false);
|
||||
err = mlx5_fs_cmd_set_tx_flow_table_root(secondary, sd->alias_obj_id,
|
||||
false);
|
||||
if (err)
|
||||
goto err_destroy_alias_ft;
|
||||
sd->tx_root_silent = false;
|
||||
|
||||
return 0;
|
||||
|
||||
err_destroy_alias_ft:
|
||||
sd_secondary_destroy_alias_ft(secondary);
|
||||
err_unset_silent:
|
||||
mlx5_fs_cmd_set_l2table_entry_silent(secondary, 0);
|
||||
if (!primary_sd->fw_silents_secondaries)
|
||||
mlx5_fs_cmd_set_l2table_entry_silent(secondary, 0);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void sd_cmd_unset_secondary(struct mlx5_core_dev *secondary)
|
||||
{
|
||||
mlx5_fs_cmd_set_tx_flow_table_root(secondary, 0, true);
|
||||
struct mlx5_sd *primary_sd;
|
||||
|
||||
primary_sd = mlx5_get_sd(mlx5_sd_get_primary(secondary));
|
||||
mlx5_sd_secondary_conf_tx_root(secondary, true);
|
||||
sd_secondary_destroy_alias_ft(secondary);
|
||||
mlx5_fs_cmd_set_l2table_entry_silent(secondary, 0);
|
||||
if (!primary_sd->fw_silents_secondaries)
|
||||
mlx5_fs_cmd_set_l2table_entry_silent(secondary, 0);
|
||||
}
|
||||
|
||||
static void sd_print_group(struct mlx5_core_dev *primary)
|
||||
@@ -672,6 +859,7 @@ int mlx5_sd_init(struct mlx5_core_dev *dev)
|
||||
peer_sd->primary_dev = NULL;
|
||||
}
|
||||
primary_sd->primary = false;
|
||||
primary_sd->next_secondary_idx = 0;
|
||||
mlx5_devcom_comp_set_ready(sd->devcom, false);
|
||||
mlx5_devcom_comp_unlock(sd->devcom);
|
||||
sd_unregister(dev);
|
||||
@@ -719,6 +907,7 @@ void mlx5_sd_cleanup(struct mlx5_core_dev *dev)
|
||||
peer_sd->primary_dev = NULL;
|
||||
}
|
||||
primary_sd->primary = false;
|
||||
primary_sd->next_secondary_idx = 0;
|
||||
out_ready_false:
|
||||
mlx5_devcom_comp_set_ready(sd->devcom, false);
|
||||
out_unlock:
|
||||
@@ -771,6 +960,127 @@ struct auxiliary_device *mlx5_sd_get_adev(struct mlx5_core_dev *dev,
|
||||
return &primary_adev->adev;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MLX5_ESWITCH
|
||||
/* All SD members must have completed esw_offloads_enable (i.e., reached
|
||||
* mlx5_esw_offloads_devcom_init) and become eswitch-peers of the primary.
|
||||
* Until then, mlx5_eswitch_is_peer() returns false for the not-yet-paired
|
||||
* member and shared_fdb_supported_filter would reject. When all PFs transition
|
||||
* in parallel, only the last one to finish satisfies this gate; the earlier
|
||||
* ones return 0 silently here.
|
||||
*/
|
||||
static bool mlx5_sd_all_paired(struct mlx5_core_dev *primary)
|
||||
{
|
||||
struct mlx5_eswitch *primary_esw = primary->priv.eswitch;
|
||||
struct mlx5_core_dev *pos;
|
||||
int i;
|
||||
|
||||
mlx5_sd_for_each_secondary(i, primary, pos) {
|
||||
if (!mlx5_eswitch_is_peer(primary_esw, pos->priv.eswitch))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
int err;
|
||||
int i;
|
||||
|
||||
ldev = mlx5_lag_dev(primary);
|
||||
if (!ldev) {
|
||||
sd_warn(primary, "Shared FDB MUST have ldev\n");
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_lock(&ldev->lock);
|
||||
|
||||
if (ldev->mode_changes_in_progress)
|
||||
goto unlock;
|
||||
|
||||
if (!mlx5_sd_all_paired(primary))
|
||||
goto unlock;
|
||||
|
||||
/* 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)
|
||||
goto unlock;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!mlx5_lag_shared_fdb_supported_filter(ldev, sd->group_id)) {
|
||||
sd_warn(primary, "Shared FDB not supported\n");
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* Initialize vport metadata for all group devices. This is deferred
|
||||
* from esw_offloads_enable() because mlx5_sd_pf_num_get() requires
|
||||
* the SD group to be ready.
|
||||
*/
|
||||
mlx5_sd_for_each_dev(i, primary, pos) {
|
||||
struct mlx5_eswitch *esw = pos->priv.eswitch;
|
||||
|
||||
err = mlx5_esw_offloads_init_deferred_metadata(esw);
|
||||
if (err) {
|
||||
sd_warn(primary, "Failed to init metadata for %s: %d\n",
|
||||
dev_name(pos->device), err);
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
|
||||
err = mlx5_lag_shared_fdb_create(ldev, NULL, 0, sd->group_id);
|
||||
if (err)
|
||||
sd_warn(primary, "Failed to create shared FDB: %d\n", err);
|
||||
else
|
||||
sd_info(primary, "Shared FDB created\n");
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&ldev->lock);
|
||||
}
|
||||
|
||||
void mlx5_sd_eswitch_mode_set(struct mlx5_core_dev *dev, u16 mlx5_mode)
|
||||
{
|
||||
struct mlx5_core_dev *primary;
|
||||
struct mlx5_sd *sd;
|
||||
int err;
|
||||
|
||||
sd = mlx5_get_sd(dev);
|
||||
if (!sd || !mlx5_devcom_comp_is_ready(sd->devcom))
|
||||
return;
|
||||
|
||||
mlx5_devcom_comp_lock(sd->devcom);
|
||||
if (!mlx5_devcom_comp_is_ready(sd->devcom))
|
||||
goto unlock;
|
||||
|
||||
primary = mlx5_sd_get_primary(dev);
|
||||
|
||||
/* Secondary devices need TX root reconfiguration */
|
||||
if (dev != primary) {
|
||||
bool disconnect = (mlx5_mode == MLX5_ESWITCH_OFFLOADS);
|
||||
|
||||
err = mlx5_sd_secondary_conf_tx_root(dev, disconnect);
|
||||
if (err) {
|
||||
sd_warn(dev, "Failed to set TX root: %d\n", err);
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to activate shared FDB when all devices are in switchdev.
|
||||
* Shared FDB is optional - failure here doesn't fail the transition.
|
||||
*/
|
||||
if (mlx5_mode == MLX5_ESWITCH_OFFLOADS)
|
||||
mlx5_sd_activate_shared_fdb(primary);
|
||||
|
||||
unlock:
|
||||
mlx5_devcom_comp_unlock(sd->devcom);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_MLX5_ESWITCH */
|
||||
|
||||
void mlx5_sd_put_adev(struct auxiliary_device *actual_adev,
|
||||
struct auxiliary_device *adev)
|
||||
{
|
||||
|
||||
@@ -12,6 +12,7 @@ struct mlx5_sd;
|
||||
|
||||
struct mlx5_core_dev *mlx5_sd_get_primary(struct mlx5_core_dev *dev);
|
||||
bool mlx5_sd_is_primary(struct mlx5_core_dev *dev);
|
||||
int mlx5_sd_pf_num_get(struct mlx5_core_dev *dev);
|
||||
struct mlx5_core_dev *mlx5_sd_primary_get_peer(struct mlx5_core_dev *primary, int idx);
|
||||
int mlx5_sd_ch_ix_get_dev_ix(struct mlx5_core_dev *dev, int ch_ix);
|
||||
int mlx5_sd_ch_ix_get_vec_ix(struct mlx5_core_dev *dev, int ch_ix);
|
||||
@@ -44,6 +45,13 @@ mlx5_sd_get_devcom(struct mlx5_core_dev *dev)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MLX5_ESWITCH
|
||||
void mlx5_sd_eswitch_mode_set(struct mlx5_core_dev *dev, u16 mlx5_mode);
|
||||
#else
|
||||
static inline void
|
||||
mlx5_sd_eswitch_mode_set(struct mlx5_core_dev *dev, u16 mlx5_mode) { return; }
|
||||
#endif
|
||||
|
||||
#define mlx5_sd_for_each_dev_from_to(i, primary, ix_from, to, pos) \
|
||||
for (i = ix_from; \
|
||||
(pos = mlx5_sd_primary_get_peer(primary, i)) && pos != (to); i++)
|
||||
|
||||
Reference in New Issue
Block a user