#include <linux/netdevice.h>
#include <linux/mlx5/driver.h>
#include <linux/mlx5/eswitch.h>
#include "mlx5_core.h"
#include "lag.h"
#include "eswitch.h"
bool mlx5_lag_shared_fdb_supported_filter(struct mlx5_lag *ldev, u32 filter)
{
int idx = mlx5_lag_get_dev_index_by_seq_filter(ldev, MLX5_LAG_P1,
filter);
struct mlx5_core_dev *dev0, *dev;
bool ret = false;
int i;
if (idx < 0)
return false;
dev0 = mlx5_lag_pf(ldev, idx)->dev;
mlx5_lag_for_each(i, 0, ldev, filter) {
if (i == idx)
continue;
dev = mlx5_lag_pf(ldev, i)->dev;
if (is_mdev_switchdev_mode(dev) &&
mlx5_eswitch_vport_match_metadata_enabled(dev->priv.eswitch) &&
MLX5_CAP_GEN(dev, lag_native_fdb_selection) &&
MLX5_CAP_ESW(dev, root_ft_on_other_esw) &&
mlx5_eswitch_is_peer(dev0->priv.eswitch, dev->priv.eswitch))
continue;
return false;
}
if (is_mdev_switchdev_mode(dev0) &&
mlx5_eswitch_vport_match_metadata_enabled(dev0->priv.eswitch) &&
mlx5_esw_offloads_devcom_is_ready(dev0->priv.eswitch) &&
MLX5_CAP_ESW(dev0, esw_shared_ingress_acl))
ret = true;
return ret;
}
bool mlx5_lag_shared_fdb_supported(struct mlx5_lag *ldev)
{
return mlx5_lag_shared_fdb_supported_filter(ldev,
MLX5_LAG_FILTER_PORTS);
}
static int mlx5_lag_create_single_fdb_filter(struct mlx5_lag *ldev, u32 filter)
{
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_single_fdb_add_one(master_esw,
slave_esw,
ldev->ports);
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_single_fdb_del_one(master_esw, slave_esw);
}
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)
{
int master_idx = mlx5_lag_get_dev_index_by_seq_filter(ldev, MLX5_LAG_P1,
filter);
struct mlx5_eswitch *master_esw;
struct mlx5_eswitch *peer_esw;
int i;
if (master_idx < 0)
return;
master_esw = mlx5_lag_pf(ldev, master_idx)->dev->priv.eswitch;
mlx5_lag_for_each(i, 0, ldev, filter) {
if (i == master_idx)
continue;
peer_esw = mlx5_lag_pf(ldev, i)->dev->priv.eswitch;
mlx5_eswitch_offloads_single_fdb_del_one(master_esw, peer_esw);
}
}
int mlx5_lag_create_single_fdb(struct mlx5_lag *ldev)
{
return mlx5_lag_create_single_fdb_filter(ldev, MLX5_LAG_FILTER_ALL);
}
void mlx5_lag_destroy_single_fdb(struct mlx5_lag *ldev)
{
mlx5_lag_destroy_single_fdb_filter(ldev, MLX5_LAG_FILTER_ALL);
}
int mlx5_lag_shared_fdb_create(struct mlx5_lag *ldev,
struct lag_tracker *tracker,
enum mlx5_lag_mode mode,
u32 group_id)
{
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;
struct lag_func *pf;
int err;
int i;
if (idx < 0)
return -EINVAL;
dev0 = mlx5_lag_pf(ldev, idx)->dev;
mlx5_lag_remove_devices_filter(ldev, filter);
if (filter == MLX5_LAG_FILTER_PORTS || filter == MLX5_LAG_FILTER_ALL) {
err = mlx5_activate_lag(ldev, tracker, mode, true);
if (err) {
mlx5_core_warn(dev0,
"Failed to create LAG in shared FDB mode (%d)\n",
err);
goto err_add_devices;
}
} else {
err = mlx5_lag_create_single_fdb_filter(ldev, group_id);
if (err) {
mlx5_core_warn(dev0,
"Failed to create SD shared FDB (%d)\n",
err);
goto err_add_devices;
}
mlx5_lag_for_each(i, 0, ldev, filter) {
pf = mlx5_lag_pf(ldev, i);
pf->sd_fdb_active = true;
}
BLOCKING_INIT_NOTIFIER_HEAD(&dev0->priv.lag_nh);
}
mlx5_lag_rescan_dev_locked(ldev, dev0, true);
err = mlx5_lag_reload_ib_reps_from_locked(ldev, 0, filter, false);
if (err) {
mlx5_core_err(dev0, "Failed to enable lag\n");
goto err_rescan_drivers;
}
if (filter == MLX5_LAG_FILTER_PORTS || filter == MLX5_LAG_FILTER_ALL)
mlx5_lag_set_vports_agg_speed(ldev);
return 0;
err_rescan_drivers:
mlx5_lag_rescan_dev_locked(ldev, dev0, false);
if (filter == MLX5_LAG_FILTER_PORTS || filter == MLX5_LAG_FILTER_ALL) {
mlx5_deactivate_lag(ldev);
} else {
mlx5_lag_for_each(i, 0, ldev, filter) {
pf = mlx5_lag_pf(ldev, i);
pf->sd_fdb_active = false;
}
mlx5_lag_destroy_single_fdb_filter(ldev, group_id);
}
err_add_devices:
mlx5_lag_add_devices_filter(ldev, filter);
mlx5_lag_reload_ib_reps_from_locked(ldev, 0, filter, true);
return err;
}
void mlx5_lag_shared_fdb_destroy(struct mlx5_lag *ldev, u32 group_id)
{
u32 filter = group_id ? group_id : MLX5_LAG_FILTER_ALL;
struct lag_func *pf;
int err;
int i;
mlx5_lag_remove_devices_filter(ldev, filter);
if (filter == MLX5_LAG_FILTER_PORTS || filter == MLX5_LAG_FILTER_ALL) {
err = mlx5_deactivate_lag(ldev);
if (err)
return;
} else {
mlx5_lag_for_each(i, 0, ldev, filter) {
pf = mlx5_lag_pf(ldev, i);
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);
mlx5_lag_reload_ib_reps_from_locked(ldev,
MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV,
filter, true);
}