xref: /linux/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c (revision fab183d632628381b466a41479489541ac0e29a0)
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