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