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
mlx5_lag_shared_fdb_supported_filter(struct mlx5_lag * ldev,u32 filter)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
mlx5_lag_shared_fdb_supported(struct mlx5_lag * ldev)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
mlx5_lag_create_single_fdb_filter(struct mlx5_lag * ldev,u32 filter)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
mlx5_lag_create_vport_lag(struct mlx5_lag * ldev,u32 group_id)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
mlx5_lag_destroy_vport_lag(struct mlx5_lag * ldev,u32 group_id)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
mlx5_lag_destroy_single_fdb_filter(struct mlx5_lag * ldev,u32 filter)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
mlx5_lag_create_single_fdb(struct mlx5_lag * ldev)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
mlx5_lag_destroy_single_fdb(struct mlx5_lag * ldev)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 */
mlx5_lag_shared_fdb_create(struct mlx5_lag * ldev,struct lag_tracker * tracker,enum mlx5_lag_mode mode,u32 group_id)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 }
274 err_add_devices:
275 mlx5_lag_add_devices_filter(ldev, filter);
276 mlx5_lag_reload_ib_reps_from_locked(ldev, 0, filter, true);
277 return err;
278 }
279
mlx5_lag_shared_fdb_destroy(struct mlx5_lag * ldev,u32 group_id)280 void mlx5_lag_shared_fdb_destroy(struct mlx5_lag *ldev, u32 group_id)
281 {
282 u32 filter = group_id ? group_id : MLX5_LAG_FILTER_ALL;
283 struct lag_func *pf;
284 int err;
285 int i;
286
287 mlx5_lag_remove_devices_filter(ldev, filter);
288
289 if (filter == MLX5_LAG_FILTER_PORTS || filter == MLX5_LAG_FILTER_ALL) {
290 err = mlx5_deactivate_lag(ldev);
291 if (err)
292 return;
293 } else {
294 mlx5_lag_for_each(i, 0, ldev, filter) {
295 pf = mlx5_lag_pf(ldev, i);
296 pf->sd_fdb_active = false;
297 }
298 mlx5_lag_destroy_single_fdb_filter(ldev, group_id);
299 mlx5_lag_unload_reps_from_locked(ldev, filter);
300 }
301
302 mlx5_lag_add_devices_filter(ldev, filter);
303 mlx5_lag_reload_ib_reps_from_locked(ldev,
304 MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV,
305 filter, true);
306 }
307