This source file includes following definitions.
- mlx5_lag_multipath_check_prereq
- __mlx5_lag_is_multipath
- mlx5_lag_is_multipath
- mlx5_lag_set_port_affinity
- mlx5_lag_fib_event_flush
- mlx5_lag_fib_route_event
- mlx5_lag_fib_nexthop_event
- mlx5_lag_fib_update
- mlx5_lag_init_fib_work
- mlx5_lag_fib_event
- mlx5_lag_mp_init
- mlx5_lag_mp_cleanup
1
2
3
4 #include <linux/netdevice.h>
5 #include <net/nexthop.h>
6 #include "lag.h"
7 #include "lag_mp.h"
8 #include "mlx5_core.h"
9 #include "eswitch.h"
10 #include "lib/mlx5.h"
11
12 static bool mlx5_lag_multipath_check_prereq(struct mlx5_lag *ldev)
13 {
14 if (!ldev->pf[0].dev || !ldev->pf[1].dev)
15 return false;
16
17 return mlx5_esw_multipath_prereq(ldev->pf[0].dev, ldev->pf[1].dev);
18 }
19
20 static bool __mlx5_lag_is_multipath(struct mlx5_lag *ldev)
21 {
22 return !!(ldev->flags & MLX5_LAG_FLAG_MULTIPATH);
23 }
24
25 bool mlx5_lag_is_multipath(struct mlx5_core_dev *dev)
26 {
27 struct mlx5_lag *ldev;
28 bool res;
29
30 ldev = mlx5_lag_dev_get(dev);
31 res = ldev && __mlx5_lag_is_multipath(ldev);
32
33 return res;
34 }
35
36
37
38
39
40
41
42
43
44
45
46 static void mlx5_lag_set_port_affinity(struct mlx5_lag *ldev, int port)
47 {
48 struct lag_tracker tracker;
49
50 if (!__mlx5_lag_is_multipath(ldev))
51 return;
52
53 switch (port) {
54 case 0:
55 tracker.netdev_state[0].tx_enabled = true;
56 tracker.netdev_state[1].tx_enabled = true;
57 tracker.netdev_state[0].link_up = true;
58 tracker.netdev_state[1].link_up = true;
59 break;
60 case 1:
61 tracker.netdev_state[0].tx_enabled = true;
62 tracker.netdev_state[0].link_up = true;
63 tracker.netdev_state[1].tx_enabled = false;
64 tracker.netdev_state[1].link_up = false;
65 break;
66 case 2:
67 tracker.netdev_state[0].tx_enabled = false;
68 tracker.netdev_state[0].link_up = false;
69 tracker.netdev_state[1].tx_enabled = true;
70 tracker.netdev_state[1].link_up = true;
71 break;
72 default:
73 mlx5_core_warn(ldev->pf[0].dev, "Invalid affinity port %d",
74 port);
75 return;
76 }
77
78 if (tracker.netdev_state[0].tx_enabled)
79 mlx5_notifier_call_chain(ldev->pf[0].dev->priv.events,
80 MLX5_DEV_EVENT_PORT_AFFINITY,
81 (void *)0);
82
83 if (tracker.netdev_state[1].tx_enabled)
84 mlx5_notifier_call_chain(ldev->pf[1].dev->priv.events,
85 MLX5_DEV_EVENT_PORT_AFFINITY,
86 (void *)0);
87
88 mlx5_modify_lag(ldev, &tracker);
89 }
90
91 static void mlx5_lag_fib_event_flush(struct notifier_block *nb)
92 {
93 struct lag_mp *mp = container_of(nb, struct lag_mp, fib_nb);
94 struct mlx5_lag *ldev = container_of(mp, struct mlx5_lag, lag_mp);
95
96 flush_workqueue(ldev->wq);
97 }
98
99 struct mlx5_fib_event_work {
100 struct work_struct work;
101 struct mlx5_lag *ldev;
102 unsigned long event;
103 union {
104 struct fib_entry_notifier_info fen_info;
105 struct fib_nh_notifier_info fnh_info;
106 };
107 };
108
109 static void mlx5_lag_fib_route_event(struct mlx5_lag *ldev,
110 unsigned long event,
111 struct fib_info *fi)
112 {
113 struct lag_mp *mp = &ldev->lag_mp;
114 struct fib_nh *fib_nh0, *fib_nh1;
115 unsigned int nhs;
116
117
118 if (event == FIB_EVENT_ENTRY_DEL) {
119
120 if (mp->mfi == fi)
121 mp->mfi = NULL;
122 return;
123 }
124
125
126 nhs = fib_info_num_path(fi);
127 if (nhs == 1) {
128 if (__mlx5_lag_is_active(ldev)) {
129 struct fib_nh *nh = fib_info_nh(fi, 0);
130 struct net_device *nh_dev = nh->fib_nh_dev;
131 int i = mlx5_lag_dev_get_netdev_idx(ldev, nh_dev);
132
133 mlx5_lag_set_port_affinity(ldev, ++i);
134 }
135 return;
136 }
137
138 if (nhs != 2)
139 return;
140
141
142 fib_nh0 = fib_info_nh(fi, 0);
143 fib_nh1 = fib_info_nh(fi, 1);
144 if (!(fib_nh0->fib_nh_dev == ldev->pf[0].netdev &&
145 fib_nh1->fib_nh_dev == ldev->pf[1].netdev) &&
146 !(fib_nh0->fib_nh_dev == ldev->pf[1].netdev &&
147 fib_nh1->fib_nh_dev == ldev->pf[0].netdev)) {
148 mlx5_core_warn(ldev->pf[0].dev, "Multipath offload require two ports of the same HCA\n");
149 return;
150 }
151
152
153 if (!mp->mfi && !__mlx5_lag_is_active(ldev)) {
154 struct lag_tracker tracker;
155
156 tracker = ldev->tracker;
157 mlx5_activate_lag(ldev, &tracker, MLX5_LAG_FLAG_MULTIPATH);
158 }
159
160 mlx5_lag_set_port_affinity(ldev, 0);
161 mp->mfi = fi;
162 }
163
164 static void mlx5_lag_fib_nexthop_event(struct mlx5_lag *ldev,
165 unsigned long event,
166 struct fib_nh *fib_nh,
167 struct fib_info *fi)
168 {
169 struct lag_mp *mp = &ldev->lag_mp;
170
171
172 if (!mp->mfi || mp->mfi != fi)
173 return;
174
175
176 if (event == FIB_EVENT_NH_DEL) {
177 int i = mlx5_lag_dev_get_netdev_idx(ldev, fib_nh->fib_nh_dev);
178
179 if (i >= 0) {
180 i = (i + 1) % 2 + 1;
181 mlx5_lag_set_port_affinity(ldev, i);
182 }
183 } else if (event == FIB_EVENT_NH_ADD &&
184 fib_info_num_path(fi) == 2) {
185 mlx5_lag_set_port_affinity(ldev, 0);
186 }
187 }
188
189 static void mlx5_lag_fib_update(struct work_struct *work)
190 {
191 struct mlx5_fib_event_work *fib_work =
192 container_of(work, struct mlx5_fib_event_work, work);
193 struct mlx5_lag *ldev = fib_work->ldev;
194 struct fib_nh *fib_nh;
195
196
197 rtnl_lock();
198 switch (fib_work->event) {
199 case FIB_EVENT_ENTRY_REPLACE:
200 case FIB_EVENT_ENTRY_APPEND:
201 case FIB_EVENT_ENTRY_ADD:
202 case FIB_EVENT_ENTRY_DEL:
203 mlx5_lag_fib_route_event(ldev, fib_work->event,
204 fib_work->fen_info.fi);
205 fib_info_put(fib_work->fen_info.fi);
206 break;
207 case FIB_EVENT_NH_ADD:
208 case FIB_EVENT_NH_DEL:
209 fib_nh = fib_work->fnh_info.fib_nh;
210 mlx5_lag_fib_nexthop_event(ldev,
211 fib_work->event,
212 fib_work->fnh_info.fib_nh,
213 fib_nh->nh_parent);
214 fib_info_put(fib_work->fnh_info.fib_nh->nh_parent);
215 break;
216 }
217
218 rtnl_unlock();
219 kfree(fib_work);
220 }
221
222 static struct mlx5_fib_event_work *
223 mlx5_lag_init_fib_work(struct mlx5_lag *ldev, unsigned long event)
224 {
225 struct mlx5_fib_event_work *fib_work;
226
227 fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC);
228 if (WARN_ON(!fib_work))
229 return NULL;
230
231 INIT_WORK(&fib_work->work, mlx5_lag_fib_update);
232 fib_work->ldev = ldev;
233 fib_work->event = event;
234
235 return fib_work;
236 }
237
238 static int mlx5_lag_fib_event(struct notifier_block *nb,
239 unsigned long event,
240 void *ptr)
241 {
242 struct lag_mp *mp = container_of(nb, struct lag_mp, fib_nb);
243 struct mlx5_lag *ldev = container_of(mp, struct mlx5_lag, lag_mp);
244 struct fib_notifier_info *info = ptr;
245 struct mlx5_fib_event_work *fib_work;
246 struct fib_entry_notifier_info *fen_info;
247 struct fib_nh_notifier_info *fnh_info;
248 struct net_device *fib_dev;
249 struct fib_info *fi;
250
251 if (!net_eq(info->net, &init_net))
252 return NOTIFY_DONE;
253
254 if (info->family != AF_INET)
255 return NOTIFY_DONE;
256
257 if (!mlx5_lag_multipath_check_prereq(ldev))
258 return NOTIFY_DONE;
259
260 switch (event) {
261 case FIB_EVENT_ENTRY_REPLACE:
262 case FIB_EVENT_ENTRY_APPEND:
263 case FIB_EVENT_ENTRY_ADD:
264 case FIB_EVENT_ENTRY_DEL:
265 fen_info = container_of(info, struct fib_entry_notifier_info,
266 info);
267 fi = fen_info->fi;
268 if (fi->nh) {
269 NL_SET_ERR_MSG_MOD(info->extack, "IPv4 route with nexthop objects is not supported");
270 return notifier_from_errno(-EINVAL);
271 }
272 fib_dev = fib_info_nh(fen_info->fi, 0)->fib_nh_dev;
273 if (fib_dev != ldev->pf[0].netdev &&
274 fib_dev != ldev->pf[1].netdev) {
275 return NOTIFY_DONE;
276 }
277 fib_work = mlx5_lag_init_fib_work(ldev, event);
278 if (!fib_work)
279 return NOTIFY_DONE;
280 fib_work->fen_info = *fen_info;
281
282
283
284 fib_info_hold(fib_work->fen_info.fi);
285 break;
286 case FIB_EVENT_NH_ADD:
287 case FIB_EVENT_NH_DEL:
288 fnh_info = container_of(info, struct fib_nh_notifier_info,
289 info);
290 fib_work = mlx5_lag_init_fib_work(ldev, event);
291 if (!fib_work)
292 return NOTIFY_DONE;
293 fib_work->fnh_info = *fnh_info;
294 fib_info_hold(fib_work->fnh_info.fib_nh->nh_parent);
295 break;
296 default:
297 return NOTIFY_DONE;
298 }
299
300 queue_work(ldev->wq, &fib_work->work);
301
302 return NOTIFY_DONE;
303 }
304
305 int mlx5_lag_mp_init(struct mlx5_lag *ldev)
306 {
307 struct lag_mp *mp = &ldev->lag_mp;
308 int err;
309
310 if (mp->fib_nb.notifier_call)
311 return 0;
312
313 mp->fib_nb.notifier_call = mlx5_lag_fib_event;
314 err = register_fib_notifier(&mp->fib_nb,
315 mlx5_lag_fib_event_flush);
316 if (err)
317 mp->fib_nb.notifier_call = NULL;
318
319 return err;
320 }
321
322 void mlx5_lag_mp_cleanup(struct mlx5_lag *ldev)
323 {
324 struct lag_mp *mp = &ldev->lag_mp;
325
326 if (!mp->fib_nb.notifier_call)
327 return;
328
329 unregister_fib_notifier(&mp->fib_nb);
330 mp->fib_nb.notifier_call = NULL;
331 }