1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB 2 /* Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */ 3 4 #include <linux/netdevice.h> 5 #include <linux/mlx5/driver.h> 6 #include <linux/mlx5/eswitch.h> 7 #include "mlx5_core.h" 8 #include "lag.h" 9 #include "eswitch.h" 10 11 bool mlx5_lag_shared_fdb_supported_filter(struct mlx5_lag *ldev, u32 filter) 12 { 13 int idx = mlx5_lag_get_dev_index_by_seq_filter(ldev, MLX5_LAG_P1, 14 filter); 15 struct mlx5_core_dev *dev0, *dev; 16 bool ret = false; 17 int i; 18 19 if (idx < 0) 20 return false; 21 22 dev0 = mlx5_lag_pf(ldev, idx)->dev; 23 mlx5_lag_for_each(i, 0, ldev, filter) { 24 if (i == idx) 25 continue; 26 dev = mlx5_lag_pf(ldev, i)->dev; 27 if (is_mdev_switchdev_mode(dev) && 28 mlx5_eswitch_vport_match_metadata_enabled(dev->priv.eswitch) && 29 MLX5_CAP_GEN(dev, lag_native_fdb_selection) && 30 MLX5_CAP_ESW(dev, root_ft_on_other_esw) && 31 mlx5_eswitch_is_peer(dev0->priv.eswitch, dev->priv.eswitch)) 32 continue; 33 return false; 34 } 35 36 if (is_mdev_switchdev_mode(dev0) && 37 mlx5_eswitch_vport_match_metadata_enabled(dev0->priv.eswitch) && 38 mlx5_esw_offloads_devcom_is_ready(dev0->priv.eswitch) && 39 MLX5_CAP_ESW(dev0, esw_shared_ingress_acl)) 40 ret = true; 41 42 return ret; 43 } 44 45 bool mlx5_lag_shared_fdb_supported(struct mlx5_lag *ldev) 46 { 47 return mlx5_lag_shared_fdb_supported_filter(ldev, 48 MLX5_LAG_FILTER_PORTS); 49 } 50 51 static int mlx5_lag_create_single_fdb_filter(struct mlx5_lag *ldev, u32 filter) 52 { 53 int master_idx = mlx5_lag_get_dev_index_by_seq_filter(ldev, MLX5_LAG_P1, 54 filter); 55 struct mlx5_eswitch *master_esw; 56 struct mlx5_core_dev *dev0; 57 int i, j; 58 int err; 59 60 if (master_idx < 0) 61 return -EINVAL; 62 63 dev0 = mlx5_lag_pf(ldev, master_idx)->dev; 64 master_esw = dev0->priv.eswitch; 65 mlx5_lag_for_each(i, 0, ldev, filter) { 66 struct mlx5_eswitch *slave_esw; 67 68 if (i == master_idx) 69 continue; 70 71 slave_esw = mlx5_lag_pf(ldev, i)->dev->priv.eswitch; 72 73 err = mlx5_eswitch_offloads_single_fdb_add_one(master_esw, 74 slave_esw, 75 ldev->ports); 76 if (err) 77 goto err; 78 } 79 return 0; 80 err: 81 mlx5_lag_for_each_reverse(j, i - 1, 0, ldev, filter) { 82 struct mlx5_eswitch *slave_esw; 83 84 if (j == master_idx) 85 continue; 86 slave_esw = mlx5_lag_pf(ldev, j)->dev->priv.eswitch; 87 mlx5_eswitch_offloads_single_fdb_del_one(master_esw, slave_esw); 88 } 89 return err; 90 } 91 92 int mlx5_lag_create_vport_lag(struct mlx5_lag *ldev, u32 group_id) 93 { 94 u32 filter = group_id ? group_id : MLX5_LAG_FILTER_ALL; 95 int master_idx = mlx5_lag_get_dev_index_by_seq_filter(ldev, MLX5_LAG_P1, 96 filter); 97 struct mlx5_eswitch *master_esw; 98 struct mlx5_core_dev *dev0; 99 int i, j; 100 int err; 101 102 if (master_idx < 0) 103 return -EINVAL; 104 105 dev0 = mlx5_lag_pf(ldev, master_idx)->dev; 106 master_esw = dev0->priv.eswitch; 107 108 mlx5_lag_for_each(i, 0, ldev, filter) { 109 struct mlx5_eswitch *slave_esw; 110 111 if (i == master_idx) 112 continue; 113 114 slave_esw = mlx5_lag_pf(ldev, i)->dev->priv.eswitch; 115 err = mlx5_eswitch_offloads_vport_lag_add_one(master_esw, 116 slave_esw); 117 if (err) 118 goto err; 119 } 120 121 return 0; 122 123 err: 124 mlx5_lag_for_each_reverse(j, i - 1, 0, ldev, filter) { 125 struct mlx5_eswitch *slave_esw; 126 127 if (j == master_idx) 128 continue; 129 slave_esw = mlx5_lag_pf(ldev, j)->dev->priv.eswitch; 130 mlx5_eswitch_offloads_vport_lag_del_one(master_esw, slave_esw); 131 } 132 return err; 133 } 134 135 int mlx5_lag_destroy_vport_lag(struct mlx5_lag *ldev, u32 group_id) 136 { 137 u32 filter = group_id ? group_id : MLX5_LAG_FILTER_ALL; 138 int master_idx = mlx5_lag_get_dev_index_by_seq_filter(ldev, MLX5_LAG_P1, 139 filter); 140 struct mlx5_eswitch *master_esw; 141 struct mlx5_core_dev *dev0; 142 int i; 143 144 if (master_idx < 0) 145 return 0; 146 147 dev0 = mlx5_lag_pf(ldev, master_idx)->dev; 148 master_esw = dev0->priv.eswitch; 149 150 mlx5_lag_for_each(i, 0, ldev, filter) { 151 struct mlx5_core_dev *dev; 152 153 if (i == master_idx) 154 continue; 155 dev = mlx5_lag_pf(ldev, i)->dev; 156 mlx5_eswitch_offloads_vport_lag_del_one(master_esw, 157 dev->priv.eswitch); 158 } 159 return 0; 160 } 161 162 static void mlx5_lag_destroy_single_fdb_filter(struct mlx5_lag *ldev, 163 u32 filter) 164 { 165 int master_idx = mlx5_lag_get_dev_index_by_seq_filter(ldev, MLX5_LAG_P1, 166 filter); 167 struct mlx5_eswitch *master_esw; 168 struct mlx5_eswitch *peer_esw; 169 int i; 170 171 if (master_idx < 0) 172 return; 173 174 master_esw = mlx5_lag_pf(ldev, master_idx)->dev->priv.eswitch; 175 mlx5_lag_for_each(i, 0, ldev, filter) { 176 if (i == master_idx) 177 continue; 178 179 peer_esw = mlx5_lag_pf(ldev, i)->dev->priv.eswitch; 180 mlx5_eswitch_offloads_single_fdb_del_one(master_esw, peer_esw); 181 } 182 } 183 184 int mlx5_lag_create_single_fdb(struct mlx5_lag *ldev) 185 { 186 return mlx5_lag_create_single_fdb_filter(ldev, MLX5_LAG_FILTER_ALL); 187 } 188 189 void mlx5_lag_destroy_single_fdb(struct mlx5_lag *ldev) 190 { 191 mlx5_lag_destroy_single_fdb_filter(ldev, MLX5_LAG_FILTER_ALL); 192 } 193 194 /** 195 * mlx5_lag_shared_fdb_create - Create shared FDB LAG 196 * @ldev: LAG device 197 * @tracker: LAG tracker (NULL for SD) 198 * @mode: LAG mode (unused for SD) 199 * @group_id: SD group ID; 0 (MLX5_LAG_FILTER_PORTS) for ports LAG; 200 * MLX5_LAG_FILTER_ALL for all-device (mpesw) LAG 201 * 202 * When group_id is 0 (MLX5_LAG_FILTER_PORTS) or MLX5_LAG_FILTER_ALL, 203 * activates a FW LAG with shared FDB. 204 * When group_id is a specific SD group ID, creates a software-only shared 205 * FDB scoped to that group (no FW LAG commands). 206 * 207 * Return: 0 on success, negative error code on failure. 208 */ 209 int mlx5_lag_shared_fdb_create(struct mlx5_lag *ldev, 210 struct lag_tracker *tracker, 211 enum mlx5_lag_mode mode, 212 u32 group_id) 213 { 214 u32 filter = group_id ? group_id : MLX5_LAG_FILTER_ALL; 215 int idx = mlx5_lag_get_dev_index_by_seq_filter(ldev, MLX5_LAG_P1, 216 filter); 217 struct mlx5_core_dev *dev0; 218 struct lag_func *pf; 219 int err; 220 int i; 221 222 if (idx < 0) 223 return -EINVAL; 224 225 dev0 = mlx5_lag_pf(ldev, idx)->dev; 226 227 mlx5_lag_remove_devices_filter(ldev, filter); 228 229 if (filter == MLX5_LAG_FILTER_PORTS || filter == MLX5_LAG_FILTER_ALL) { 230 err = mlx5_activate_lag(ldev, tracker, mode, true); 231 if (err) { 232 mlx5_core_warn(dev0, 233 "Failed to create LAG in shared FDB mode (%d)\n", 234 err); 235 goto err_add_devices; 236 } 237 } else { 238 err = mlx5_lag_create_single_fdb_filter(ldev, group_id); 239 if (err) { 240 mlx5_core_warn(dev0, 241 "Failed to create SD shared FDB (%d)\n", 242 err); 243 goto err_add_devices; 244 } 245 mlx5_lag_for_each(i, 0, ldev, filter) { 246 pf = mlx5_lag_pf(ldev, i); 247 pf->sd_fdb_active = true; 248 } 249 BLOCKING_INIT_NOTIFIER_HEAD(&dev0->priv.lag_nh); 250 } 251 252 mlx5_lag_rescan_dev_locked(ldev, dev0, true); 253 err = mlx5_lag_reload_ib_reps_from_locked(ldev, 0, filter, false); 254 if (err) { 255 mlx5_core_err(dev0, "Failed to enable lag\n"); 256 goto err_rescan_drivers; 257 } 258 259 if (filter == MLX5_LAG_FILTER_PORTS || filter == MLX5_LAG_FILTER_ALL) 260 mlx5_lag_set_vports_agg_speed(ldev); 261 return 0; 262 263 err_rescan_drivers: 264 mlx5_lag_rescan_dev_locked(ldev, dev0, false); 265 if (filter == MLX5_LAG_FILTER_PORTS || filter == MLX5_LAG_FILTER_ALL) { 266 mlx5_deactivate_lag(ldev); 267 } else { 268 mlx5_lag_for_each(i, 0, ldev, filter) { 269 pf = mlx5_lag_pf(ldev, i); 270 pf->sd_fdb_active = false; 271 } 272 mlx5_lag_destroy_single_fdb_filter(ldev, group_id); 273 mlx5_lag_unload_reps_from_locked(ldev, filter); 274 } 275 err_add_devices: 276 mlx5_lag_add_devices_filter(ldev, filter); 277 mlx5_lag_reload_ib_reps_from_locked(ldev, 0, filter, true); 278 return err; 279 } 280 281 void mlx5_lag_shared_fdb_destroy(struct mlx5_lag *ldev, u32 group_id) 282 { 283 u32 filter = group_id ? group_id : MLX5_LAG_FILTER_ALL; 284 struct lag_func *pf; 285 int err; 286 int i; 287 288 mlx5_lag_remove_devices_filter(ldev, filter); 289 290 if (filter == MLX5_LAG_FILTER_PORTS || filter == MLX5_LAG_FILTER_ALL) { 291 err = mlx5_deactivate_lag(ldev); 292 if (err) 293 return; 294 } else { 295 mlx5_lag_for_each(i, 0, ldev, filter) { 296 pf = mlx5_lag_pf(ldev, i); 297 pf->sd_fdb_active = false; 298 } 299 mlx5_lag_destroy_single_fdb_filter(ldev, group_id); 300 mlx5_lag_unload_reps_from_locked(ldev, filter); 301 } 302 303 mlx5_lag_add_devices_filter(ldev, filter); 304 mlx5_lag_reload_ib_reps_from_locked(ldev, 305 MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV, 306 filter, true); 307 } 308