mlxsw: spectrum_router: Refactor virtual router handling
[linux-2.6-block.git] / drivers / net / ethernet / mellanox / mlxsw / spectrum_router.c
CommitLineData
464dce18
IS
1/*
2 * drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
3 * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
4 * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
5 * Copyright (c) 2016 Ido Schimmel <idosch@mellanox.com>
c723c735 6 * Copyright (c) 2016 Yotam Gigi <yotamg@mellanox.com>
464dce18
IS
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the names of the copyright holders nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * Alternatively, this software may be distributed under the terms of the
21 * GNU General Public License ("GPL") version 2 as published by the Free
22 * Software Foundation.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
35 */
36
37#include <linux/kernel.h>
38#include <linux/types.h>
5e9c16cc
JP
39#include <linux/rhashtable.h>
40#include <linux/bitops.h>
41#include <linux/in6.h>
c723c735 42#include <linux/notifier.h>
df6dd79b 43#include <linux/inetdevice.h>
c723c735 44#include <net/netevent.h>
6cf3c971
JP
45#include <net/neighbour.h>
46#include <net/arp.h>
b45f64d1 47#include <net/ip_fib.h>
464dce18
IS
48
49#include "spectrum.h"
50#include "core.h"
51#include "reg.h"
52
4724ba56
IS
53struct mlxsw_sp_rif {
54 struct list_head nexthop_list;
55 struct list_head neigh_list;
56 struct net_device *dev;
57 struct mlxsw_sp_fid *f;
58 unsigned char addr[ETH_ALEN];
59 int mtu;
60 u16 rif;
61};
62
63static struct mlxsw_sp_rif *
64mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
65 const struct net_device *dev);
66
53342023
JP
67#define mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) \
68 for_each_set_bit(prefix, (prefix_usage)->b, MLXSW_SP_PREFIX_COUNT)
69
6b75c480
JP
70static bool
71mlxsw_sp_prefix_usage_subset(struct mlxsw_sp_prefix_usage *prefix_usage1,
72 struct mlxsw_sp_prefix_usage *prefix_usage2)
73{
74 unsigned char prefix;
75
76 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage1) {
77 if (!test_bit(prefix, prefix_usage2->b))
78 return false;
79 }
80 return true;
81}
82
53342023
JP
83static bool
84mlxsw_sp_prefix_usage_eq(struct mlxsw_sp_prefix_usage *prefix_usage1,
85 struct mlxsw_sp_prefix_usage *prefix_usage2)
86{
87 return !memcmp(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
88}
89
6b75c480
JP
90static bool
91mlxsw_sp_prefix_usage_none(struct mlxsw_sp_prefix_usage *prefix_usage)
92{
93 struct mlxsw_sp_prefix_usage prefix_usage_none = {{ 0 } };
94
95 return mlxsw_sp_prefix_usage_eq(prefix_usage, &prefix_usage_none);
96}
97
98static void
99mlxsw_sp_prefix_usage_cpy(struct mlxsw_sp_prefix_usage *prefix_usage1,
100 struct mlxsw_sp_prefix_usage *prefix_usage2)
101{
102 memcpy(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
103}
104
5e9c16cc
JP
105static void
106mlxsw_sp_prefix_usage_set(struct mlxsw_sp_prefix_usage *prefix_usage,
107 unsigned char prefix_len)
108{
109 set_bit(prefix_len, prefix_usage->b);
110}
111
112static void
113mlxsw_sp_prefix_usage_clear(struct mlxsw_sp_prefix_usage *prefix_usage,
114 unsigned char prefix_len)
115{
116 clear_bit(prefix_len, prefix_usage->b);
117}
118
119struct mlxsw_sp_fib_key {
120 unsigned char addr[sizeof(struct in6_addr)];
121 unsigned char prefix_len;
122};
123
61c503f9
JP
124enum mlxsw_sp_fib_entry_type {
125 MLXSW_SP_FIB_ENTRY_TYPE_REMOTE,
126 MLXSW_SP_FIB_ENTRY_TYPE_LOCAL,
127 MLXSW_SP_FIB_ENTRY_TYPE_TRAP,
128};
129
a7ff87ac
JP
130struct mlxsw_sp_nexthop_group;
131
9aecce1c
IS
132struct mlxsw_sp_fib_node {
133 struct list_head entry_list;
b45f64d1 134 struct list_head list;
9aecce1c 135 struct rhash_head ht_node;
76610ebb 136 struct mlxsw_sp_fib *fib;
5e9c16cc 137 struct mlxsw_sp_fib_key key;
9aecce1c
IS
138};
139
140struct mlxsw_sp_fib_entry_params {
141 u32 tb_id;
142 u32 prio;
143 u8 tos;
144 u8 type;
145};
146
147struct mlxsw_sp_fib_entry {
148 struct list_head list;
149 struct mlxsw_sp_fib_node *fib_node;
61c503f9 150 enum mlxsw_sp_fib_entry_type type;
a7ff87ac
JP
151 struct list_head nexthop_group_node;
152 struct mlxsw_sp_nexthop_group *nh_group;
9aecce1c 153 struct mlxsw_sp_fib_entry_params params;
013b20f9 154 bool offloaded;
5e9c16cc
JP
155};
156
157struct mlxsw_sp_fib {
158 struct rhashtable ht;
9aecce1c 159 struct list_head node_list;
76610ebb
IS
160 struct mlxsw_sp_vr *vr;
161 struct mlxsw_sp_lpm_tree *lpm_tree;
5e9c16cc
JP
162 unsigned long prefix_ref_count[MLXSW_SP_PREFIX_COUNT];
163 struct mlxsw_sp_prefix_usage prefix_usage;
76610ebb 164 enum mlxsw_sp_l3proto proto;
5e9c16cc
JP
165};
166
9aecce1c 167static const struct rhashtable_params mlxsw_sp_fib_ht_params;
5e9c16cc 168
76610ebb
IS
169static struct mlxsw_sp_fib *mlxsw_sp_fib_create(struct mlxsw_sp_vr *vr,
170 enum mlxsw_sp_l3proto proto)
5e9c16cc
JP
171{
172 struct mlxsw_sp_fib *fib;
173 int err;
174
175 fib = kzalloc(sizeof(*fib), GFP_KERNEL);
176 if (!fib)
177 return ERR_PTR(-ENOMEM);
178 err = rhashtable_init(&fib->ht, &mlxsw_sp_fib_ht_params);
179 if (err)
180 goto err_rhashtable_init;
9aecce1c 181 INIT_LIST_HEAD(&fib->node_list);
76610ebb
IS
182 fib->proto = proto;
183 fib->vr = vr;
5e9c16cc
JP
184 return fib;
185
186err_rhashtable_init:
187 kfree(fib);
188 return ERR_PTR(err);
189}
190
191static void mlxsw_sp_fib_destroy(struct mlxsw_sp_fib *fib)
192{
9aecce1c 193 WARN_ON(!list_empty(&fib->node_list));
76610ebb 194 WARN_ON(fib->lpm_tree);
5e9c16cc
JP
195 rhashtable_destroy(&fib->ht);
196 kfree(fib);
197}
198
53342023 199static struct mlxsw_sp_lpm_tree *
382dbb40 200mlxsw_sp_lpm_tree_find_unused(struct mlxsw_sp *mlxsw_sp)
53342023
JP
201{
202 static struct mlxsw_sp_lpm_tree *lpm_tree;
203 int i;
204
205 for (i = 0; i < MLXSW_SP_LPM_TREE_COUNT; i++) {
206 lpm_tree = &mlxsw_sp->router.lpm_trees[i];
382dbb40
IS
207 if (lpm_tree->ref_count == 0)
208 return lpm_tree;
53342023
JP
209 }
210 return NULL;
211}
212
213static int mlxsw_sp_lpm_tree_alloc(struct mlxsw_sp *mlxsw_sp,
214 struct mlxsw_sp_lpm_tree *lpm_tree)
215{
216 char ralta_pl[MLXSW_REG_RALTA_LEN];
217
1a9234e6
IS
218 mlxsw_reg_ralta_pack(ralta_pl, true,
219 (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
220 lpm_tree->id);
53342023
JP
221 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
222}
223
224static int mlxsw_sp_lpm_tree_free(struct mlxsw_sp *mlxsw_sp,
225 struct mlxsw_sp_lpm_tree *lpm_tree)
226{
227 char ralta_pl[MLXSW_REG_RALTA_LEN];
228
1a9234e6
IS
229 mlxsw_reg_ralta_pack(ralta_pl, false,
230 (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
231 lpm_tree->id);
53342023
JP
232 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
233}
234
235static int
236mlxsw_sp_lpm_tree_left_struct_set(struct mlxsw_sp *mlxsw_sp,
237 struct mlxsw_sp_prefix_usage *prefix_usage,
238 struct mlxsw_sp_lpm_tree *lpm_tree)
239{
240 char ralst_pl[MLXSW_REG_RALST_LEN];
241 u8 root_bin = 0;
242 u8 prefix;
243 u8 last_prefix = MLXSW_REG_RALST_BIN_NO_CHILD;
244
245 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage)
246 root_bin = prefix;
247
248 mlxsw_reg_ralst_pack(ralst_pl, root_bin, lpm_tree->id);
249 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) {
250 if (prefix == 0)
251 continue;
252 mlxsw_reg_ralst_bin_pack(ralst_pl, prefix, last_prefix,
253 MLXSW_REG_RALST_BIN_NO_CHILD);
254 last_prefix = prefix;
255 }
256 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
257}
258
259static struct mlxsw_sp_lpm_tree *
260mlxsw_sp_lpm_tree_create(struct mlxsw_sp *mlxsw_sp,
261 struct mlxsw_sp_prefix_usage *prefix_usage,
382dbb40 262 enum mlxsw_sp_l3proto proto)
53342023
JP
263{
264 struct mlxsw_sp_lpm_tree *lpm_tree;
265 int err;
266
382dbb40 267 lpm_tree = mlxsw_sp_lpm_tree_find_unused(mlxsw_sp);
53342023
JP
268 if (!lpm_tree)
269 return ERR_PTR(-EBUSY);
270 lpm_tree->proto = proto;
271 err = mlxsw_sp_lpm_tree_alloc(mlxsw_sp, lpm_tree);
272 if (err)
273 return ERR_PTR(err);
274
275 err = mlxsw_sp_lpm_tree_left_struct_set(mlxsw_sp, prefix_usage,
276 lpm_tree);
277 if (err)
278 goto err_left_struct_set;
2083d367
JP
279 memcpy(&lpm_tree->prefix_usage, prefix_usage,
280 sizeof(lpm_tree->prefix_usage));
53342023
JP
281 return lpm_tree;
282
283err_left_struct_set:
284 mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
285 return ERR_PTR(err);
286}
287
288static int mlxsw_sp_lpm_tree_destroy(struct mlxsw_sp *mlxsw_sp,
289 struct mlxsw_sp_lpm_tree *lpm_tree)
290{
291 return mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
292}
293
294static struct mlxsw_sp_lpm_tree *
295mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp,
296 struct mlxsw_sp_prefix_usage *prefix_usage,
382dbb40 297 enum mlxsw_sp_l3proto proto)
53342023
JP
298{
299 struct mlxsw_sp_lpm_tree *lpm_tree;
300 int i;
301
302 for (i = 0; i < MLXSW_SP_LPM_TREE_COUNT; i++) {
303 lpm_tree = &mlxsw_sp->router.lpm_trees[i];
8b99becd
JP
304 if (lpm_tree->ref_count != 0 &&
305 lpm_tree->proto == proto &&
53342023
JP
306 mlxsw_sp_prefix_usage_eq(&lpm_tree->prefix_usage,
307 prefix_usage))
308 goto inc_ref_count;
309 }
310 lpm_tree = mlxsw_sp_lpm_tree_create(mlxsw_sp, prefix_usage,
382dbb40 311 proto);
53342023
JP
312 if (IS_ERR(lpm_tree))
313 return lpm_tree;
314
315inc_ref_count:
316 lpm_tree->ref_count++;
317 return lpm_tree;
318}
319
320static int mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp,
321 struct mlxsw_sp_lpm_tree *lpm_tree)
322{
323 if (--lpm_tree->ref_count == 0)
324 return mlxsw_sp_lpm_tree_destroy(mlxsw_sp, lpm_tree);
325 return 0;
326}
327
328static void mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp)
329{
330 struct mlxsw_sp_lpm_tree *lpm_tree;
331 int i;
332
333 for (i = 0; i < MLXSW_SP_LPM_TREE_COUNT; i++) {
334 lpm_tree = &mlxsw_sp->router.lpm_trees[i];
335 lpm_tree->id = i + MLXSW_SP_LPM_TREE_MIN;
336 }
337}
338
76610ebb
IS
339static bool mlxsw_sp_vr_is_used(const struct mlxsw_sp_vr *vr)
340{
341 return !!vr->fib4;
342}
343
6b75c480
JP
344static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp)
345{
346 struct mlxsw_sp_vr *vr;
347 int i;
348
c1a38311 349 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
6b75c480 350 vr = &mlxsw_sp->router.vrs[i];
76610ebb 351 if (!mlxsw_sp_vr_is_used(vr))
6b75c480
JP
352 return vr;
353 }
354 return NULL;
355}
356
357static int mlxsw_sp_vr_lpm_tree_bind(struct mlxsw_sp *mlxsw_sp,
76610ebb 358 const struct mlxsw_sp_fib *fib)
6b75c480
JP
359{
360 char raltb_pl[MLXSW_REG_RALTB_LEN];
361
76610ebb
IS
362 mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,
363 (enum mlxsw_reg_ralxx_protocol) fib->proto,
364 fib->lpm_tree->id);
6b75c480
JP
365 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
366}
367
368static int mlxsw_sp_vr_lpm_tree_unbind(struct mlxsw_sp *mlxsw_sp,
76610ebb 369 const struct mlxsw_sp_fib *fib)
6b75c480
JP
370{
371 char raltb_pl[MLXSW_REG_RALTB_LEN];
372
373 /* Bind to tree 0 which is default */
76610ebb
IS
374 mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,
375 (enum mlxsw_reg_ralxx_protocol) fib->proto, 0);
6b75c480
JP
376 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
377}
378
379static u32 mlxsw_sp_fix_tb_id(u32 tb_id)
380{
381 /* For our purpose, squash main and local table into one */
382 if (tb_id == RT_TABLE_LOCAL)
383 tb_id = RT_TABLE_MAIN;
384 return tb_id;
385}
386
387static struct mlxsw_sp_vr *mlxsw_sp_vr_find(struct mlxsw_sp *mlxsw_sp,
76610ebb 388 u32 tb_id)
6b75c480
JP
389{
390 struct mlxsw_sp_vr *vr;
391 int i;
392
393 tb_id = mlxsw_sp_fix_tb_id(tb_id);
9497c042 394
c1a38311 395 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
6b75c480 396 vr = &mlxsw_sp->router.vrs[i];
76610ebb 397 if (mlxsw_sp_vr_is_used(vr) && vr->tb_id == tb_id)
6b75c480
JP
398 return vr;
399 }
400 return NULL;
401}
402
76610ebb
IS
403static struct mlxsw_sp_fib *mlxsw_sp_vr_fib(const struct mlxsw_sp_vr *vr,
404 enum mlxsw_sp_l3proto proto)
405{
406 switch (proto) {
407 case MLXSW_SP_L3_PROTO_IPV4:
408 return vr->fib4;
409 case MLXSW_SP_L3_PROTO_IPV6:
410 BUG_ON(1);
411 }
412 return NULL;
413}
414
6b75c480 415static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp,
76610ebb 416 u32 tb_id)
6b75c480 417{
6b75c480 418 struct mlxsw_sp_vr *vr;
6b75c480
JP
419
420 vr = mlxsw_sp_vr_find_unused(mlxsw_sp);
421 if (!vr)
422 return ERR_PTR(-EBUSY);
76610ebb
IS
423 vr->fib4 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV4);
424 if (IS_ERR(vr->fib4))
425 return ERR_CAST(vr->fib4);
6b75c480 426 vr->tb_id = tb_id;
6b75c480 427 return vr;
6b75c480
JP
428}
429
76610ebb 430static void mlxsw_sp_vr_destroy(struct mlxsw_sp_vr *vr)
6b75c480 431{
76610ebb
IS
432 mlxsw_sp_fib_destroy(vr->fib4);
433 vr->fib4 = NULL;
6b75c480
JP
434}
435
436static int
76610ebb 437mlxsw_sp_vr_lpm_tree_check(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib *fib,
6b75c480
JP
438 struct mlxsw_sp_prefix_usage *req_prefix_usage)
439{
76610ebb 440 struct mlxsw_sp_lpm_tree *lpm_tree = fib->lpm_tree;
f7df4923
IS
441 struct mlxsw_sp_lpm_tree *new_tree;
442 int err;
6b75c480 443
f7df4923 444 if (mlxsw_sp_prefix_usage_eq(req_prefix_usage, &lpm_tree->prefix_usage))
6b75c480
JP
445 return 0;
446
f7df4923 447 new_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, req_prefix_usage,
76610ebb 448 fib->proto);
f7df4923 449 if (IS_ERR(new_tree)) {
6b75c480
JP
450 /* We failed to get a tree according to the required
451 * prefix usage. However, the current tree might be still good
452 * for us if our requirement is subset of the prefixes used
453 * in the tree.
454 */
455 if (mlxsw_sp_prefix_usage_subset(req_prefix_usage,
f7df4923 456 &lpm_tree->prefix_usage))
6b75c480 457 return 0;
f7df4923 458 return PTR_ERR(new_tree);
6b75c480
JP
459 }
460
f7df4923 461 /* Prevent packet loss by overwriting existing binding */
76610ebb
IS
462 fib->lpm_tree = new_tree;
463 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib);
f7df4923
IS
464 if (err)
465 goto err_tree_bind;
466 mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree);
467
468 return 0;
469
470err_tree_bind:
76610ebb 471 fib->lpm_tree = lpm_tree;
f7df4923
IS
472 mlxsw_sp_lpm_tree_put(mlxsw_sp, new_tree);
473 return err;
6b75c480
JP
474}
475
76610ebb 476static struct mlxsw_sp_vr *mlxsw_sp_vr_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id)
6b75c480
JP
477{
478 struct mlxsw_sp_vr *vr;
6b75c480
JP
479
480 tb_id = mlxsw_sp_fix_tb_id(tb_id);
76610ebb
IS
481 vr = mlxsw_sp_vr_find(mlxsw_sp, tb_id);
482 if (!vr)
483 vr = mlxsw_sp_vr_create(mlxsw_sp, tb_id);
6b75c480
JP
484 return vr;
485}
486
76610ebb 487static void mlxsw_sp_vr_put(struct mlxsw_sp_vr *vr)
6b75c480 488{
76610ebb
IS
489 if (list_empty(&vr->fib4->node_list))
490 mlxsw_sp_vr_destroy(vr);
6b75c480
JP
491}
492
9497c042 493static int mlxsw_sp_vrs_init(struct mlxsw_sp *mlxsw_sp)
6b75c480
JP
494{
495 struct mlxsw_sp_vr *vr;
c1a38311 496 u64 max_vrs;
6b75c480
JP
497 int i;
498
c1a38311 499 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_VRS))
9497c042
NF
500 return -EIO;
501
c1a38311
JP
502 max_vrs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS);
503 mlxsw_sp->router.vrs = kcalloc(max_vrs, sizeof(struct mlxsw_sp_vr),
9497c042
NF
504 GFP_KERNEL);
505 if (!mlxsw_sp->router.vrs)
506 return -ENOMEM;
507
c1a38311 508 for (i = 0; i < max_vrs; i++) {
6b75c480
JP
509 vr = &mlxsw_sp->router.vrs[i];
510 vr->id = i;
511 }
9497c042
NF
512
513 return 0;
514}
515
ac571de9
IS
516static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp);
517
9497c042
NF
518static void mlxsw_sp_vrs_fini(struct mlxsw_sp *mlxsw_sp)
519{
3057224e
IS
520 /* At this stage we're guaranteed not to have new incoming
521 * FIB notifications and the work queue is free from FIBs
522 * sitting on top of mlxsw netdevs. However, we can still
523 * have other FIBs queued. Flush the queue before flushing
524 * the device's tables. No need for locks, as we're the only
525 * writer.
526 */
527 mlxsw_core_flush_owq();
ac571de9 528 mlxsw_sp_router_fib_flush(mlxsw_sp);
9497c042 529 kfree(mlxsw_sp->router.vrs);
6b75c480
JP
530}
531
6cf3c971 532struct mlxsw_sp_neigh_key {
33b1341c 533 struct neighbour *n;
6cf3c971
JP
534};
535
536struct mlxsw_sp_neigh_entry {
9665b745 537 struct list_head rif_list_node;
6cf3c971
JP
538 struct rhash_head ht_node;
539 struct mlxsw_sp_neigh_key key;
540 u16 rif;
5c8802f1 541 bool connected;
a6bf9e93 542 unsigned char ha[ETH_ALEN];
a7ff87ac
JP
543 struct list_head nexthop_list; /* list of nexthops using
544 * this neigh entry
545 */
b2157149 546 struct list_head nexthop_neighs_list_node;
6cf3c971
JP
547};
548
549static const struct rhashtable_params mlxsw_sp_neigh_ht_params = {
550 .key_offset = offsetof(struct mlxsw_sp_neigh_entry, key),
551 .head_offset = offsetof(struct mlxsw_sp_neigh_entry, ht_node),
552 .key_len = sizeof(struct mlxsw_sp_neigh_key),
553};
554
6cf3c971 555static struct mlxsw_sp_neigh_entry *
5c8802f1
IS
556mlxsw_sp_neigh_entry_alloc(struct mlxsw_sp *mlxsw_sp, struct neighbour *n,
557 u16 rif)
6cf3c971
JP
558{
559 struct mlxsw_sp_neigh_entry *neigh_entry;
560
5c8802f1 561 neigh_entry = kzalloc(sizeof(*neigh_entry), GFP_KERNEL);
6cf3c971
JP
562 if (!neigh_entry)
563 return NULL;
5c8802f1 564
33b1341c 565 neigh_entry->key.n = n;
6cf3c971 566 neigh_entry->rif = rif;
a7ff87ac 567 INIT_LIST_HEAD(&neigh_entry->nexthop_list);
5c8802f1 568
6cf3c971
JP
569 return neigh_entry;
570}
571
5c8802f1 572static void mlxsw_sp_neigh_entry_free(struct mlxsw_sp_neigh_entry *neigh_entry)
6cf3c971
JP
573{
574 kfree(neigh_entry);
575}
576
5c8802f1
IS
577static int
578mlxsw_sp_neigh_entry_insert(struct mlxsw_sp *mlxsw_sp,
579 struct mlxsw_sp_neigh_entry *neigh_entry)
6cf3c971 580{
5c8802f1
IS
581 return rhashtable_insert_fast(&mlxsw_sp->router.neigh_ht,
582 &neigh_entry->ht_node,
583 mlxsw_sp_neigh_ht_params);
584}
6cf3c971 585
5c8802f1
IS
586static void
587mlxsw_sp_neigh_entry_remove(struct mlxsw_sp *mlxsw_sp,
588 struct mlxsw_sp_neigh_entry *neigh_entry)
589{
590 rhashtable_remove_fast(&mlxsw_sp->router.neigh_ht,
591 &neigh_entry->ht_node,
592 mlxsw_sp_neigh_ht_params);
6cf3c971
JP
593}
594
5c8802f1
IS
595static struct mlxsw_sp_neigh_entry *
596mlxsw_sp_neigh_entry_create(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
6cf3c971 597{
6cf3c971
JP
598 struct mlxsw_sp_neigh_entry *neigh_entry;
599 struct mlxsw_sp_rif *r;
6cf3c971
JP
600 int err;
601
51af96b5 602 r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, n->dev);
5c8802f1
IS
603 if (!r)
604 return ERR_PTR(-EINVAL);
6cf3c971 605
5c8802f1 606 neigh_entry = mlxsw_sp_neigh_entry_alloc(mlxsw_sp, n, r->rif);
6cf3c971 607 if (!neigh_entry)
5c8802f1
IS
608 return ERR_PTR(-ENOMEM);
609
6cf3c971
JP
610 err = mlxsw_sp_neigh_entry_insert(mlxsw_sp, neigh_entry);
611 if (err)
612 goto err_neigh_entry_insert;
5c8802f1 613
9665b745
IS
614 list_add(&neigh_entry->rif_list_node, &r->neigh_list);
615
5c8802f1 616 return neigh_entry;
6cf3c971
JP
617
618err_neigh_entry_insert:
5c8802f1
IS
619 mlxsw_sp_neigh_entry_free(neigh_entry);
620 return ERR_PTR(err);
6cf3c971
JP
621}
622
5c8802f1
IS
623static void
624mlxsw_sp_neigh_entry_destroy(struct mlxsw_sp *mlxsw_sp,
625 struct mlxsw_sp_neigh_entry *neigh_entry)
6cf3c971 626{
9665b745 627 list_del(&neigh_entry->rif_list_node);
5c8802f1
IS
628 mlxsw_sp_neigh_entry_remove(mlxsw_sp, neigh_entry);
629 mlxsw_sp_neigh_entry_free(neigh_entry);
630}
6cf3c971 631
5c8802f1
IS
632static struct mlxsw_sp_neigh_entry *
633mlxsw_sp_neigh_entry_lookup(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
634{
635 struct mlxsw_sp_neigh_key key;
6cf3c971 636
5c8802f1
IS
637 key.n = n;
638 return rhashtable_lookup_fast(&mlxsw_sp->router.neigh_ht,
639 &key, mlxsw_sp_neigh_ht_params);
6cf3c971
JP
640}
641
c723c735
YG
642static void
643mlxsw_sp_router_neighs_update_interval_init(struct mlxsw_sp *mlxsw_sp)
644{
645 unsigned long interval = NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME);
646
647 mlxsw_sp->router.neighs_update.interval = jiffies_to_msecs(interval);
648}
649
650static void mlxsw_sp_router_neigh_ent_ipv4_process(struct mlxsw_sp *mlxsw_sp,
651 char *rauhtd_pl,
652 int ent_index)
653{
654 struct net_device *dev;
655 struct neighbour *n;
656 __be32 dipn;
657 u32 dip;
658 u16 rif;
659
660 mlxsw_reg_rauhtd_ent_ipv4_unpack(rauhtd_pl, ent_index, &rif, &dip);
661
662 if (!mlxsw_sp->rifs[rif]) {
663 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
664 return;
665 }
666
667 dipn = htonl(dip);
668 dev = mlxsw_sp->rifs[rif]->dev;
669 n = neigh_lookup(&arp_tbl, &dipn, dev);
670 if (!n) {
671 netdev_err(dev, "Failed to find matching neighbour for IP=%pI4h\n",
672 &dip);
673 return;
674 }
675
676 netdev_dbg(dev, "Updating neighbour with IP=%pI4h\n", &dip);
677 neigh_event_send(n, NULL);
678 neigh_release(n);
679}
680
681static void mlxsw_sp_router_neigh_rec_ipv4_process(struct mlxsw_sp *mlxsw_sp,
682 char *rauhtd_pl,
683 int rec_index)
684{
685 u8 num_entries;
686 int i;
687
688 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
689 rec_index);
690 /* Hardware starts counting at 0, so add 1. */
691 num_entries++;
692
693 /* Each record consists of several neighbour entries. */
694 for (i = 0; i < num_entries; i++) {
695 int ent_index;
696
697 ent_index = rec_index * MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC + i;
698 mlxsw_sp_router_neigh_ent_ipv4_process(mlxsw_sp, rauhtd_pl,
699 ent_index);
700 }
701
702}
703
704static void mlxsw_sp_router_neigh_rec_process(struct mlxsw_sp *mlxsw_sp,
705 char *rauhtd_pl, int rec_index)
706{
707 switch (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, rec_index)) {
708 case MLXSW_REG_RAUHTD_TYPE_IPV4:
709 mlxsw_sp_router_neigh_rec_ipv4_process(mlxsw_sp, rauhtd_pl,
710 rec_index);
711 break;
712 case MLXSW_REG_RAUHTD_TYPE_IPV6:
713 WARN_ON_ONCE(1);
714 break;
715 }
716}
717
42cdb338
AS
718static bool mlxsw_sp_router_rauhtd_is_full(char *rauhtd_pl)
719{
720 u8 num_rec, last_rec_index, num_entries;
721
722 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
723 last_rec_index = num_rec - 1;
724
725 if (num_rec < MLXSW_REG_RAUHTD_REC_MAX_NUM)
726 return false;
727 if (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, last_rec_index) ==
728 MLXSW_REG_RAUHTD_TYPE_IPV6)
729 return true;
730
731 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
732 last_rec_index);
733 if (++num_entries == MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC)
734 return true;
735 return false;
736}
737
b2157149 738static int mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp)
c723c735 739{
c723c735
YG
740 char *rauhtd_pl;
741 u8 num_rec;
742 int i, err;
743
744 rauhtd_pl = kmalloc(MLXSW_REG_RAUHTD_LEN, GFP_KERNEL);
745 if (!rauhtd_pl)
b2157149 746 return -ENOMEM;
c723c735
YG
747
748 /* Make sure the neighbour's netdev isn't removed in the
749 * process.
750 */
751 rtnl_lock();
752 do {
753 mlxsw_reg_rauhtd_pack(rauhtd_pl, MLXSW_REG_RAUHTD_TYPE_IPV4);
754 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(rauhtd),
755 rauhtd_pl);
756 if (err) {
757 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to dump neighbour talbe\n");
758 break;
759 }
760 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
761 for (i = 0; i < num_rec; i++)
762 mlxsw_sp_router_neigh_rec_process(mlxsw_sp, rauhtd_pl,
763 i);
42cdb338 764 } while (mlxsw_sp_router_rauhtd_is_full(rauhtd_pl));
c723c735
YG
765 rtnl_unlock();
766
767 kfree(rauhtd_pl);
b2157149
YG
768 return err;
769}
770
771static void mlxsw_sp_router_neighs_update_nh(struct mlxsw_sp *mlxsw_sp)
772{
773 struct mlxsw_sp_neigh_entry *neigh_entry;
774
775 /* Take RTNL mutex here to prevent lists from changes */
776 rtnl_lock();
777 list_for_each_entry(neigh_entry, &mlxsw_sp->router.nexthop_neighs_list,
8a0b7275 778 nexthop_neighs_list_node)
b2157149
YG
779 /* If this neigh have nexthops, make the kernel think this neigh
780 * is active regardless of the traffic.
781 */
8a0b7275 782 neigh_event_send(neigh_entry->key.n, NULL);
b2157149
YG
783 rtnl_unlock();
784}
785
786static void
787mlxsw_sp_router_neighs_update_work_schedule(struct mlxsw_sp *mlxsw_sp)
788{
789 unsigned long interval = mlxsw_sp->router.neighs_update.interval;
790
791 mlxsw_core_schedule_dw(&mlxsw_sp->router.neighs_update.dw,
792 msecs_to_jiffies(interval));
793}
794
795static void mlxsw_sp_router_neighs_update_work(struct work_struct *work)
796{
797 struct mlxsw_sp *mlxsw_sp = container_of(work, struct mlxsw_sp,
798 router.neighs_update.dw.work);
799 int err;
800
801 err = mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp);
802 if (err)
803 dev_err(mlxsw_sp->bus_info->dev, "Could not update kernel for neigh activity");
804
805 mlxsw_sp_router_neighs_update_nh(mlxsw_sp);
806
c723c735
YG
807 mlxsw_sp_router_neighs_update_work_schedule(mlxsw_sp);
808}
809
0b2361d9
YG
810static void mlxsw_sp_router_probe_unresolved_nexthops(struct work_struct *work)
811{
812 struct mlxsw_sp_neigh_entry *neigh_entry;
813 struct mlxsw_sp *mlxsw_sp = container_of(work, struct mlxsw_sp,
814 router.nexthop_probe_dw.work);
815
816 /* Iterate over nexthop neighbours, find those who are unresolved and
817 * send arp on them. This solves the chicken-egg problem when
818 * the nexthop wouldn't get offloaded until the neighbor is resolved
819 * but it wouldn't get resolved ever in case traffic is flowing in HW
820 * using different nexthop.
821 *
822 * Take RTNL mutex here to prevent lists from changes.
823 */
824 rtnl_lock();
825 list_for_each_entry(neigh_entry, &mlxsw_sp->router.nexthop_neighs_list,
8a0b7275 826 nexthop_neighs_list_node)
01b1aa35 827 if (!neigh_entry->connected)
33b1341c 828 neigh_event_send(neigh_entry->key.n, NULL);
0b2361d9
YG
829 rtnl_unlock();
830
831 mlxsw_core_schedule_dw(&mlxsw_sp->router.nexthop_probe_dw,
832 MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL);
833}
834
a7ff87ac
JP
835static void
836mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
837 struct mlxsw_sp_neigh_entry *neigh_entry,
838 bool removing);
839
5c8802f1
IS
840static enum mlxsw_reg_rauht_op mlxsw_sp_rauht_op(bool adding)
841{
842 return adding ? MLXSW_REG_RAUHT_OP_WRITE_ADD :
843 MLXSW_REG_RAUHT_OP_WRITE_DELETE;
844}
845
846static void
847mlxsw_sp_router_neigh_entry_op4(struct mlxsw_sp *mlxsw_sp,
848 struct mlxsw_sp_neigh_entry *neigh_entry,
849 enum mlxsw_reg_rauht_op op)
a6bf9e93 850{
33b1341c 851 struct neighbour *n = neigh_entry->key.n;
5c8802f1 852 u32 dip = ntohl(*((__be32 *) n->primary_key));
a6bf9e93 853 char rauht_pl[MLXSW_REG_RAUHT_LEN];
5c8802f1
IS
854
855 mlxsw_reg_rauht_pack4(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
856 dip);
857 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
858}
859
860static void
861mlxsw_sp_neigh_entry_update(struct mlxsw_sp *mlxsw_sp,
862 struct mlxsw_sp_neigh_entry *neigh_entry,
863 bool adding)
864{
865 if (!adding && !neigh_entry->connected)
866 return;
867 neigh_entry->connected = adding;
868 if (neigh_entry->key.n->tbl == &arp_tbl)
869 mlxsw_sp_router_neigh_entry_op4(mlxsw_sp, neigh_entry,
870 mlxsw_sp_rauht_op(adding));
871 else
872 WARN_ON_ONCE(1);
873}
874
875struct mlxsw_sp_neigh_event_work {
876 struct work_struct work;
877 struct mlxsw_sp *mlxsw_sp;
878 struct neighbour *n;
879};
880
881static void mlxsw_sp_router_neigh_event_work(struct work_struct *work)
882{
883 struct mlxsw_sp_neigh_event_work *neigh_work =
884 container_of(work, struct mlxsw_sp_neigh_event_work, work);
885 struct mlxsw_sp *mlxsw_sp = neigh_work->mlxsw_sp;
886 struct mlxsw_sp_neigh_entry *neigh_entry;
887 struct neighbour *n = neigh_work->n;
888 unsigned char ha[ETH_ALEN];
a6bf9e93 889 bool entry_connected;
93a87e5e 890 u8 nud_state, dead;
a6bf9e93 891
5c8802f1
IS
892 /* If these parameters are changed after we release the lock,
893 * then we are guaranteed to receive another event letting us
894 * know about it.
895 */
a6bf9e93 896 read_lock_bh(&n->lock);
5c8802f1 897 memcpy(ha, n->ha, ETH_ALEN);
a6bf9e93 898 nud_state = n->nud_state;
93a87e5e 899 dead = n->dead;
a6bf9e93
YG
900 read_unlock_bh(&n->lock);
901
5c8802f1 902 rtnl_lock();
93a87e5e 903 entry_connected = nud_state & NUD_VALID && !dead;
5c8802f1
IS
904 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
905 if (!entry_connected && !neigh_entry)
906 goto out;
907 if (!neigh_entry) {
908 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
909 if (IS_ERR(neigh_entry))
910 goto out;
a6bf9e93
YG
911 }
912
5c8802f1
IS
913 memcpy(neigh_entry->ha, ha, ETH_ALEN);
914 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, entry_connected);
915 mlxsw_sp_nexthop_neigh_update(mlxsw_sp, neigh_entry, !entry_connected);
916
917 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
918 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
919
920out:
921 rtnl_unlock();
a6bf9e93 922 neigh_release(n);
5c8802f1 923 kfree(neigh_work);
a6bf9e93
YG
924}
925
e7322638
JP
926int mlxsw_sp_router_netevent_event(struct notifier_block *unused,
927 unsigned long event, void *ptr)
c723c735 928{
5c8802f1 929 struct mlxsw_sp_neigh_event_work *neigh_work;
c723c735
YG
930 struct mlxsw_sp_port *mlxsw_sp_port;
931 struct mlxsw_sp *mlxsw_sp;
932 unsigned long interval;
933 struct neigh_parms *p;
a6bf9e93 934 struct neighbour *n;
c723c735
YG
935
936 switch (event) {
937 case NETEVENT_DELAY_PROBE_TIME_UPDATE:
938 p = ptr;
939
940 /* We don't care about changes in the default table. */
941 if (!p->dev || p->tbl != &arp_tbl)
942 return NOTIFY_DONE;
943
944 /* We are in atomic context and can't take RTNL mutex,
945 * so use RCU variant to walk the device chain.
946 */
947 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(p->dev);
948 if (!mlxsw_sp_port)
949 return NOTIFY_DONE;
950
951 mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
952 interval = jiffies_to_msecs(NEIGH_VAR(p, DELAY_PROBE_TIME));
953 mlxsw_sp->router.neighs_update.interval = interval;
954
955 mlxsw_sp_port_dev_put(mlxsw_sp_port);
956 break;
a6bf9e93
YG
957 case NETEVENT_NEIGH_UPDATE:
958 n = ptr;
a6bf9e93
YG
959
960 if (n->tbl != &arp_tbl)
961 return NOTIFY_DONE;
962
5c8802f1 963 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(n->dev);
a6bf9e93
YG
964 if (!mlxsw_sp_port)
965 return NOTIFY_DONE;
966
5c8802f1
IS
967 neigh_work = kzalloc(sizeof(*neigh_work), GFP_ATOMIC);
968 if (!neigh_work) {
a6bf9e93 969 mlxsw_sp_port_dev_put(mlxsw_sp_port);
5c8802f1 970 return NOTIFY_BAD;
a6bf9e93 971 }
5c8802f1
IS
972
973 INIT_WORK(&neigh_work->work, mlxsw_sp_router_neigh_event_work);
974 neigh_work->mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
975 neigh_work->n = n;
a6bf9e93
YG
976
977 /* Take a reference to ensure the neighbour won't be
978 * destructed until we drop the reference in delayed
979 * work.
980 */
981 neigh_clone(n);
5c8802f1
IS
982 mlxsw_core_schedule_work(&neigh_work->work);
983 mlxsw_sp_port_dev_put(mlxsw_sp_port);
a6bf9e93 984 break;
c723c735
YG
985 }
986
987 return NOTIFY_DONE;
988}
989
6cf3c971
JP
990static int mlxsw_sp_neigh_init(struct mlxsw_sp *mlxsw_sp)
991{
c723c735
YG
992 int err;
993
994 err = rhashtable_init(&mlxsw_sp->router.neigh_ht,
995 &mlxsw_sp_neigh_ht_params);
996 if (err)
997 return err;
998
999 /* Initialize the polling interval according to the default
1000 * table.
1001 */
1002 mlxsw_sp_router_neighs_update_interval_init(mlxsw_sp);
1003
0b2361d9 1004 /* Create the delayed works for the activity_update */
c723c735
YG
1005 INIT_DELAYED_WORK(&mlxsw_sp->router.neighs_update.dw,
1006 mlxsw_sp_router_neighs_update_work);
0b2361d9
YG
1007 INIT_DELAYED_WORK(&mlxsw_sp->router.nexthop_probe_dw,
1008 mlxsw_sp_router_probe_unresolved_nexthops);
c723c735 1009 mlxsw_core_schedule_dw(&mlxsw_sp->router.neighs_update.dw, 0);
0b2361d9 1010 mlxsw_core_schedule_dw(&mlxsw_sp->router.nexthop_probe_dw, 0);
c723c735 1011 return 0;
6cf3c971
JP
1012}
1013
1014static void mlxsw_sp_neigh_fini(struct mlxsw_sp *mlxsw_sp)
1015{
c723c735 1016 cancel_delayed_work_sync(&mlxsw_sp->router.neighs_update.dw);
0b2361d9 1017 cancel_delayed_work_sync(&mlxsw_sp->router.nexthop_probe_dw);
6cf3c971
JP
1018 rhashtable_destroy(&mlxsw_sp->router.neigh_ht);
1019}
1020
9665b745
IS
1021static int mlxsw_sp_neigh_rif_flush(struct mlxsw_sp *mlxsw_sp,
1022 const struct mlxsw_sp_rif *r)
1023{
1024 char rauht_pl[MLXSW_REG_RAUHT_LEN];
1025
1026 mlxsw_reg_rauht_pack(rauht_pl, MLXSW_REG_RAUHT_OP_WRITE_DELETE_ALL,
1027 r->rif, r->addr);
1028 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
1029}
1030
1031static void mlxsw_sp_neigh_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
1032 struct mlxsw_sp_rif *r)
1033{
1034 struct mlxsw_sp_neigh_entry *neigh_entry, *tmp;
1035
1036 mlxsw_sp_neigh_rif_flush(mlxsw_sp, r);
1037 list_for_each_entry_safe(neigh_entry, tmp, &r->neigh_list,
1038 rif_list_node)
1039 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
1040}
1041
c53b8e1b
IS
1042struct mlxsw_sp_nexthop_key {
1043 struct fib_nh *fib_nh;
1044};
1045
a7ff87ac
JP
1046struct mlxsw_sp_nexthop {
1047 struct list_head neigh_list_node; /* member of neigh entry list */
9665b745 1048 struct list_head rif_list_node;
a7ff87ac
JP
1049 struct mlxsw_sp_nexthop_group *nh_grp; /* pointer back to the group
1050 * this belongs to
1051 */
c53b8e1b
IS
1052 struct rhash_head ht_node;
1053 struct mlxsw_sp_nexthop_key key;
b8399a1e 1054 struct mlxsw_sp_rif *r;
a7ff87ac
JP
1055 u8 should_offload:1, /* set indicates this neigh is connected and
1056 * should be put to KVD linear area of this group.
1057 */
1058 offloaded:1, /* set in case the neigh is actually put into
1059 * KVD linear area of this group.
1060 */
1061 update:1; /* set indicates that MAC of this neigh should be
1062 * updated in HW
1063 */
1064 struct mlxsw_sp_neigh_entry *neigh_entry;
1065};
1066
e9ad5e7d
IS
1067struct mlxsw_sp_nexthop_group_key {
1068 struct fib_info *fi;
1069};
1070
a7ff87ac 1071struct mlxsw_sp_nexthop_group {
e9ad5e7d 1072 struct rhash_head ht_node;
a7ff87ac 1073 struct list_head fib_list; /* list of fib entries that use this group */
e9ad5e7d 1074 struct mlxsw_sp_nexthop_group_key key;
b3e8d1eb
IS
1075 u8 adj_index_valid:1,
1076 gateway:1; /* routes using the group use a gateway */
a7ff87ac
JP
1077 u32 adj_index;
1078 u16 ecmp_size;
1079 u16 count;
1080 struct mlxsw_sp_nexthop nexthops[0];
b8399a1e 1081#define nh_rif nexthops[0].r
a7ff87ac
JP
1082};
1083
e9ad5e7d
IS
1084static const struct rhashtable_params mlxsw_sp_nexthop_group_ht_params = {
1085 .key_offset = offsetof(struct mlxsw_sp_nexthop_group, key),
1086 .head_offset = offsetof(struct mlxsw_sp_nexthop_group, ht_node),
1087 .key_len = sizeof(struct mlxsw_sp_nexthop_group_key),
1088};
1089
1090static int mlxsw_sp_nexthop_group_insert(struct mlxsw_sp *mlxsw_sp,
1091 struct mlxsw_sp_nexthop_group *nh_grp)
1092{
1093 return rhashtable_insert_fast(&mlxsw_sp->router.nexthop_group_ht,
1094 &nh_grp->ht_node,
1095 mlxsw_sp_nexthop_group_ht_params);
1096}
1097
1098static void mlxsw_sp_nexthop_group_remove(struct mlxsw_sp *mlxsw_sp,
1099 struct mlxsw_sp_nexthop_group *nh_grp)
1100{
1101 rhashtable_remove_fast(&mlxsw_sp->router.nexthop_group_ht,
1102 &nh_grp->ht_node,
1103 mlxsw_sp_nexthop_group_ht_params);
1104}
1105
1106static struct mlxsw_sp_nexthop_group *
1107mlxsw_sp_nexthop_group_lookup(struct mlxsw_sp *mlxsw_sp,
1108 struct mlxsw_sp_nexthop_group_key key)
1109{
1110 return rhashtable_lookup_fast(&mlxsw_sp->router.nexthop_group_ht, &key,
1111 mlxsw_sp_nexthop_group_ht_params);
1112}
1113
c53b8e1b
IS
1114static const struct rhashtable_params mlxsw_sp_nexthop_ht_params = {
1115 .key_offset = offsetof(struct mlxsw_sp_nexthop, key),
1116 .head_offset = offsetof(struct mlxsw_sp_nexthop, ht_node),
1117 .key_len = sizeof(struct mlxsw_sp_nexthop_key),
1118};
1119
1120static int mlxsw_sp_nexthop_insert(struct mlxsw_sp *mlxsw_sp,
1121 struct mlxsw_sp_nexthop *nh)
1122{
1123 return rhashtable_insert_fast(&mlxsw_sp->router.nexthop_ht,
1124 &nh->ht_node, mlxsw_sp_nexthop_ht_params);
1125}
1126
1127static void mlxsw_sp_nexthop_remove(struct mlxsw_sp *mlxsw_sp,
1128 struct mlxsw_sp_nexthop *nh)
1129{
1130 rhashtable_remove_fast(&mlxsw_sp->router.nexthop_ht, &nh->ht_node,
1131 mlxsw_sp_nexthop_ht_params);
1132}
1133
ad178c8e
IS
1134static struct mlxsw_sp_nexthop *
1135mlxsw_sp_nexthop_lookup(struct mlxsw_sp *mlxsw_sp,
1136 struct mlxsw_sp_nexthop_key key)
1137{
1138 return rhashtable_lookup_fast(&mlxsw_sp->router.nexthop_ht, &key,
1139 mlxsw_sp_nexthop_ht_params);
1140}
1141
a7ff87ac 1142static int mlxsw_sp_adj_index_mass_update_vr(struct mlxsw_sp *mlxsw_sp,
76610ebb 1143 const struct mlxsw_sp_fib *fib,
a7ff87ac
JP
1144 u32 adj_index, u16 ecmp_size,
1145 u32 new_adj_index,
1146 u16 new_ecmp_size)
1147{
1148 char raleu_pl[MLXSW_REG_RALEU_LEN];
1149
1a9234e6 1150 mlxsw_reg_raleu_pack(raleu_pl,
76610ebb
IS
1151 (enum mlxsw_reg_ralxx_protocol) fib->proto,
1152 fib->vr->id, adj_index, ecmp_size, new_adj_index,
1a9234e6 1153 new_ecmp_size);
a7ff87ac
JP
1154 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raleu), raleu_pl);
1155}
1156
1157static int mlxsw_sp_adj_index_mass_update(struct mlxsw_sp *mlxsw_sp,
1158 struct mlxsw_sp_nexthop_group *nh_grp,
1159 u32 old_adj_index, u16 old_ecmp_size)
1160{
1161 struct mlxsw_sp_fib_entry *fib_entry;
76610ebb 1162 struct mlxsw_sp_fib *fib = NULL;
a7ff87ac
JP
1163 int err;
1164
1165 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
76610ebb 1166 if (fib == fib_entry->fib_node->fib)
a7ff87ac 1167 continue;
76610ebb
IS
1168 fib = fib_entry->fib_node->fib;
1169 err = mlxsw_sp_adj_index_mass_update_vr(mlxsw_sp, fib,
a7ff87ac
JP
1170 old_adj_index,
1171 old_ecmp_size,
1172 nh_grp->adj_index,
1173 nh_grp->ecmp_size);
1174 if (err)
1175 return err;
1176 }
1177 return 0;
1178}
1179
1180static int mlxsw_sp_nexthop_mac_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index,
1181 struct mlxsw_sp_nexthop *nh)
1182{
1183 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
1184 char ratr_pl[MLXSW_REG_RATR_LEN];
1185
1186 mlxsw_reg_ratr_pack(ratr_pl, MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY,
1187 true, adj_index, neigh_entry->rif);
1188 mlxsw_reg_ratr_eth_entry_pack(ratr_pl, neigh_entry->ha);
1189 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl);
1190}
1191
1192static int
1193mlxsw_sp_nexthop_group_mac_update(struct mlxsw_sp *mlxsw_sp,
a59b7e02
IS
1194 struct mlxsw_sp_nexthop_group *nh_grp,
1195 bool reallocate)
a7ff87ac
JP
1196{
1197 u32 adj_index = nh_grp->adj_index; /* base */
1198 struct mlxsw_sp_nexthop *nh;
1199 int i;
1200 int err;
1201
1202 for (i = 0; i < nh_grp->count; i++) {
1203 nh = &nh_grp->nexthops[i];
1204
1205 if (!nh->should_offload) {
1206 nh->offloaded = 0;
1207 continue;
1208 }
1209
a59b7e02 1210 if (nh->update || reallocate) {
a7ff87ac
JP
1211 err = mlxsw_sp_nexthop_mac_update(mlxsw_sp,
1212 adj_index, nh);
1213 if (err)
1214 return err;
1215 nh->update = 0;
1216 nh->offloaded = 1;
1217 }
1218 adj_index++;
1219 }
1220 return 0;
1221}
1222
1223static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
1224 struct mlxsw_sp_fib_entry *fib_entry);
1225
1226static int
1227mlxsw_sp_nexthop_fib_entries_update(struct mlxsw_sp *mlxsw_sp,
1228 struct mlxsw_sp_nexthop_group *nh_grp)
1229{
1230 struct mlxsw_sp_fib_entry *fib_entry;
1231 int err;
1232
1233 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
1234 err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
1235 if (err)
1236 return err;
1237 }
1238 return 0;
1239}
1240
1241static void
1242mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp,
1243 struct mlxsw_sp_nexthop_group *nh_grp)
1244{
1245 struct mlxsw_sp_nexthop *nh;
1246 bool offload_change = false;
1247 u32 adj_index;
1248 u16 ecmp_size = 0;
1249 bool old_adj_index_valid;
1250 u32 old_adj_index;
1251 u16 old_ecmp_size;
1252 int ret;
1253 int i;
1254 int err;
1255
b3e8d1eb
IS
1256 if (!nh_grp->gateway) {
1257 mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
1258 return;
1259 }
1260
a7ff87ac
JP
1261 for (i = 0; i < nh_grp->count; i++) {
1262 nh = &nh_grp->nexthops[i];
1263
1264 if (nh->should_offload ^ nh->offloaded) {
1265 offload_change = true;
1266 if (nh->should_offload)
1267 nh->update = 1;
1268 }
1269 if (nh->should_offload)
1270 ecmp_size++;
1271 }
1272 if (!offload_change) {
1273 /* Nothing was added or removed, so no need to reallocate. Just
1274 * update MAC on existing adjacency indexes.
1275 */
a59b7e02
IS
1276 err = mlxsw_sp_nexthop_group_mac_update(mlxsw_sp, nh_grp,
1277 false);
a7ff87ac
JP
1278 if (err) {
1279 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
1280 goto set_trap;
1281 }
1282 return;
1283 }
1284 if (!ecmp_size)
1285 /* No neigh of this group is connected so we just set
1286 * the trap and let everthing flow through kernel.
1287 */
1288 goto set_trap;
1289
1290 ret = mlxsw_sp_kvdl_alloc(mlxsw_sp, ecmp_size);
1291 if (ret < 0) {
1292 /* We ran out of KVD linear space, just set the
1293 * trap and let everything flow through kernel.
1294 */
1295 dev_warn(mlxsw_sp->bus_info->dev, "Failed to allocate KVD linear area for nexthop group.\n");
1296 goto set_trap;
1297 }
1298 adj_index = ret;
1299 old_adj_index_valid = nh_grp->adj_index_valid;
1300 old_adj_index = nh_grp->adj_index;
1301 old_ecmp_size = nh_grp->ecmp_size;
1302 nh_grp->adj_index_valid = 1;
1303 nh_grp->adj_index = adj_index;
1304 nh_grp->ecmp_size = ecmp_size;
a59b7e02 1305 err = mlxsw_sp_nexthop_group_mac_update(mlxsw_sp, nh_grp, true);
a7ff87ac
JP
1306 if (err) {
1307 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
1308 goto set_trap;
1309 }
1310
1311 if (!old_adj_index_valid) {
1312 /* The trap was set for fib entries, so we have to call
1313 * fib entry update to unset it and use adjacency index.
1314 */
1315 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
1316 if (err) {
1317 dev_warn(mlxsw_sp->bus_info->dev, "Failed to add adjacency index to fib entries.\n");
1318 goto set_trap;
1319 }
1320 return;
1321 }
1322
1323 err = mlxsw_sp_adj_index_mass_update(mlxsw_sp, nh_grp,
1324 old_adj_index, old_ecmp_size);
1325 mlxsw_sp_kvdl_free(mlxsw_sp, old_adj_index);
1326 if (err) {
1327 dev_warn(mlxsw_sp->bus_info->dev, "Failed to mass-update adjacency index for nexthop group.\n");
1328 goto set_trap;
1329 }
1330 return;
1331
1332set_trap:
1333 old_adj_index_valid = nh_grp->adj_index_valid;
1334 nh_grp->adj_index_valid = 0;
1335 for (i = 0; i < nh_grp->count; i++) {
1336 nh = &nh_grp->nexthops[i];
1337 nh->offloaded = 0;
1338 }
1339 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
1340 if (err)
1341 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set traps for fib entries.\n");
1342 if (old_adj_index_valid)
1343 mlxsw_sp_kvdl_free(mlxsw_sp, nh_grp->adj_index);
1344}
1345
1346static void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh,
1347 bool removing)
1348{
1349 if (!removing && !nh->should_offload)
1350 nh->should_offload = 1;
1351 else if (removing && nh->offloaded)
1352 nh->should_offload = 0;
1353 nh->update = 1;
1354}
1355
1356static void
1357mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
1358 struct mlxsw_sp_neigh_entry *neigh_entry,
1359 bool removing)
1360{
1361 struct mlxsw_sp_nexthop *nh;
1362
a7ff87ac
JP
1363 list_for_each_entry(nh, &neigh_entry->nexthop_list,
1364 neigh_list_node) {
1365 __mlxsw_sp_nexthop_neigh_update(nh, removing);
1366 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
1367 }
a7ff87ac
JP
1368}
1369
9665b745
IS
1370static void mlxsw_sp_nexthop_rif_init(struct mlxsw_sp_nexthop *nh,
1371 struct mlxsw_sp_rif *r)
1372{
1373 if (nh->r)
1374 return;
1375
1376 nh->r = r;
1377 list_add(&nh->rif_list_node, &r->nexthop_list);
1378}
1379
1380static void mlxsw_sp_nexthop_rif_fini(struct mlxsw_sp_nexthop *nh)
1381{
1382 if (!nh->r)
1383 return;
1384
1385 list_del(&nh->rif_list_node);
1386 nh->r = NULL;
1387}
1388
a8c97014
IS
1389static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp,
1390 struct mlxsw_sp_nexthop *nh)
a7ff87ac
JP
1391{
1392 struct mlxsw_sp_neigh_entry *neigh_entry;
a8c97014 1393 struct fib_nh *fib_nh = nh->key.fib_nh;
a7ff87ac 1394 struct neighbour *n;
93a87e5e 1395 u8 nud_state, dead;
c53b8e1b
IS
1396 int err;
1397
ad178c8e 1398 if (!nh->nh_grp->gateway || nh->neigh_entry)
b8399a1e
IS
1399 return 0;
1400
33b1341c
JP
1401 /* Take a reference of neigh here ensuring that neigh would
1402 * not be detructed before the nexthop entry is finished.
1403 * The reference is taken either in neigh_lookup() or
fd76d910 1404 * in neigh_create() in case n is not found.
33b1341c 1405 */
a8c97014 1406 n = neigh_lookup(&arp_tbl, &fib_nh->nh_gw, fib_nh->nh_dev);
33b1341c 1407 if (!n) {
a8c97014
IS
1408 n = neigh_create(&arp_tbl, &fib_nh->nh_gw, fib_nh->nh_dev);
1409 if (IS_ERR(n))
1410 return PTR_ERR(n);
a7ff87ac 1411 neigh_event_send(n, NULL);
33b1341c
JP
1412 }
1413 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
1414 if (!neigh_entry) {
5c8802f1
IS
1415 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
1416 if (IS_ERR(neigh_entry)) {
c53b8e1b
IS
1417 err = -EINVAL;
1418 goto err_neigh_entry_create;
5c8802f1 1419 }
a7ff87ac 1420 }
b2157149
YG
1421
1422 /* If that is the first nexthop connected to that neigh, add to
1423 * nexthop_neighs_list
1424 */
1425 if (list_empty(&neigh_entry->nexthop_list))
1426 list_add_tail(&neigh_entry->nexthop_neighs_list_node,
1427 &mlxsw_sp->router.nexthop_neighs_list);
1428
a7ff87ac
JP
1429 nh->neigh_entry = neigh_entry;
1430 list_add_tail(&nh->neigh_list_node, &neigh_entry->nexthop_list);
1431 read_lock_bh(&n->lock);
1432 nud_state = n->nud_state;
93a87e5e 1433 dead = n->dead;
a7ff87ac 1434 read_unlock_bh(&n->lock);
93a87e5e 1435 __mlxsw_sp_nexthop_neigh_update(nh, !(nud_state & NUD_VALID && !dead));
a7ff87ac
JP
1436
1437 return 0;
c53b8e1b
IS
1438
1439err_neigh_entry_create:
1440 neigh_release(n);
c53b8e1b 1441 return err;
a7ff87ac
JP
1442}
1443
a8c97014
IS
1444static void mlxsw_sp_nexthop_neigh_fini(struct mlxsw_sp *mlxsw_sp,
1445 struct mlxsw_sp_nexthop *nh)
a7ff87ac
JP
1446{
1447 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
a8c97014 1448 struct neighbour *n;
a7ff87ac 1449
b8399a1e 1450 if (!neigh_entry)
a8c97014
IS
1451 return;
1452 n = neigh_entry->key.n;
b8399a1e 1453
58312125 1454 __mlxsw_sp_nexthop_neigh_update(nh, true);
a7ff87ac 1455 list_del(&nh->neigh_list_node);
e58be79e 1456 nh->neigh_entry = NULL;
b2157149
YG
1457
1458 /* If that is the last nexthop connected to that neigh, remove from
1459 * nexthop_neighs_list
1460 */
e58be79e
IS
1461 if (list_empty(&neigh_entry->nexthop_list))
1462 list_del(&neigh_entry->nexthop_neighs_list_node);
b2157149 1463
5c8802f1
IS
1464 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
1465 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
1466
1467 neigh_release(n);
a8c97014 1468}
c53b8e1b 1469
a8c97014
IS
1470static int mlxsw_sp_nexthop_init(struct mlxsw_sp *mlxsw_sp,
1471 struct mlxsw_sp_nexthop_group *nh_grp,
1472 struct mlxsw_sp_nexthop *nh,
1473 struct fib_nh *fib_nh)
1474{
1475 struct net_device *dev = fib_nh->nh_dev;
df6dd79b 1476 struct in_device *in_dev;
a8c97014
IS
1477 struct mlxsw_sp_rif *r;
1478 int err;
1479
1480 nh->nh_grp = nh_grp;
1481 nh->key.fib_nh = fib_nh;
1482 err = mlxsw_sp_nexthop_insert(mlxsw_sp, nh);
1483 if (err)
1484 return err;
1485
97989ee0
IS
1486 if (!dev)
1487 return 0;
1488
df6dd79b
IS
1489 in_dev = __in_dev_get_rtnl(dev);
1490 if (in_dev && IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) &&
1491 fib_nh->nh_flags & RTNH_F_LINKDOWN)
1492 return 0;
1493
a8c97014
IS
1494 r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
1495 if (!r)
1496 return 0;
9665b745 1497 mlxsw_sp_nexthop_rif_init(nh, r);
a8c97014
IS
1498
1499 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
1500 if (err)
1501 goto err_nexthop_neigh_init;
1502
1503 return 0;
1504
1505err_nexthop_neigh_init:
1506 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
1507 return err;
1508}
1509
1510static void mlxsw_sp_nexthop_fini(struct mlxsw_sp *mlxsw_sp,
1511 struct mlxsw_sp_nexthop *nh)
1512{
1513 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
9665b745 1514 mlxsw_sp_nexthop_rif_fini(nh);
c53b8e1b 1515 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
a7ff87ac
JP
1516}
1517
ad178c8e
IS
1518static void mlxsw_sp_nexthop_event(struct mlxsw_sp *mlxsw_sp,
1519 unsigned long event, struct fib_nh *fib_nh)
1520{
1521 struct mlxsw_sp_nexthop_key key;
1522 struct mlxsw_sp_nexthop *nh;
1523 struct mlxsw_sp_rif *r;
1524
1525 if (mlxsw_sp->router.aborted)
1526 return;
1527
1528 key.fib_nh = fib_nh;
1529 nh = mlxsw_sp_nexthop_lookup(mlxsw_sp, key);
1530 if (WARN_ON_ONCE(!nh))
1531 return;
1532
1533 r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, fib_nh->nh_dev);
1534 if (!r)
1535 return;
1536
1537 switch (event) {
1538 case FIB_EVENT_NH_ADD:
9665b745 1539 mlxsw_sp_nexthop_rif_init(nh, r);
ad178c8e
IS
1540 mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
1541 break;
1542 case FIB_EVENT_NH_DEL:
1543 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
9665b745 1544 mlxsw_sp_nexthop_rif_fini(nh);
ad178c8e
IS
1545 break;
1546 }
1547
1548 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
1549}
1550
9665b745
IS
1551static void mlxsw_sp_nexthop_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
1552 struct mlxsw_sp_rif *r)
1553{
1554 struct mlxsw_sp_nexthop *nh, *tmp;
1555
1556 list_for_each_entry_safe(nh, tmp, &r->nexthop_list, rif_list_node) {
1557 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
1558 mlxsw_sp_nexthop_rif_fini(nh);
1559 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
1560 }
1561}
1562
a7ff87ac
JP
1563static struct mlxsw_sp_nexthop_group *
1564mlxsw_sp_nexthop_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi)
1565{
1566 struct mlxsw_sp_nexthop_group *nh_grp;
1567 struct mlxsw_sp_nexthop *nh;
1568 struct fib_nh *fib_nh;
1569 size_t alloc_size;
1570 int i;
1571 int err;
1572
1573 alloc_size = sizeof(*nh_grp) +
1574 fi->fib_nhs * sizeof(struct mlxsw_sp_nexthop);
1575 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
1576 if (!nh_grp)
1577 return ERR_PTR(-ENOMEM);
1578 INIT_LIST_HEAD(&nh_grp->fib_list);
b3e8d1eb 1579 nh_grp->gateway = fi->fib_nh->nh_scope == RT_SCOPE_LINK;
a7ff87ac 1580 nh_grp->count = fi->fib_nhs;
e9ad5e7d 1581 nh_grp->key.fi = fi;
a7ff87ac
JP
1582 for (i = 0; i < nh_grp->count; i++) {
1583 nh = &nh_grp->nexthops[i];
1584 fib_nh = &fi->fib_nh[i];
1585 err = mlxsw_sp_nexthop_init(mlxsw_sp, nh_grp, nh, fib_nh);
1586 if (err)
1587 goto err_nexthop_init;
1588 }
e9ad5e7d
IS
1589 err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
1590 if (err)
1591 goto err_nexthop_group_insert;
a7ff87ac
JP
1592 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
1593 return nh_grp;
1594
e9ad5e7d 1595err_nexthop_group_insert:
a7ff87ac 1596err_nexthop_init:
df6dd79b
IS
1597 for (i--; i >= 0; i--) {
1598 nh = &nh_grp->nexthops[i];
a7ff87ac 1599 mlxsw_sp_nexthop_fini(mlxsw_sp, nh);
df6dd79b 1600 }
a7ff87ac
JP
1601 kfree(nh_grp);
1602 return ERR_PTR(err);
1603}
1604
1605static void
1606mlxsw_sp_nexthop_group_destroy(struct mlxsw_sp *mlxsw_sp,
1607 struct mlxsw_sp_nexthop_group *nh_grp)
1608{
1609 struct mlxsw_sp_nexthop *nh;
1610 int i;
1611
e9ad5e7d 1612 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
a7ff87ac
JP
1613 for (i = 0; i < nh_grp->count; i++) {
1614 nh = &nh_grp->nexthops[i];
1615 mlxsw_sp_nexthop_fini(mlxsw_sp, nh);
1616 }
58312125
IS
1617 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
1618 WARN_ON_ONCE(nh_grp->adj_index_valid);
a7ff87ac
JP
1619 kfree(nh_grp);
1620}
1621
a7ff87ac
JP
1622static int mlxsw_sp_nexthop_group_get(struct mlxsw_sp *mlxsw_sp,
1623 struct mlxsw_sp_fib_entry *fib_entry,
1624 struct fib_info *fi)
1625{
e9ad5e7d 1626 struct mlxsw_sp_nexthop_group_key key;
a7ff87ac
JP
1627 struct mlxsw_sp_nexthop_group *nh_grp;
1628
e9ad5e7d
IS
1629 key.fi = fi;
1630 nh_grp = mlxsw_sp_nexthop_group_lookup(mlxsw_sp, key);
a7ff87ac
JP
1631 if (!nh_grp) {
1632 nh_grp = mlxsw_sp_nexthop_group_create(mlxsw_sp, fi);
1633 if (IS_ERR(nh_grp))
1634 return PTR_ERR(nh_grp);
1635 }
1636 list_add_tail(&fib_entry->nexthop_group_node, &nh_grp->fib_list);
1637 fib_entry->nh_group = nh_grp;
1638 return 0;
1639}
1640
1641static void mlxsw_sp_nexthop_group_put(struct mlxsw_sp *mlxsw_sp,
1642 struct mlxsw_sp_fib_entry *fib_entry)
1643{
1644 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
1645
1646 list_del(&fib_entry->nexthop_group_node);
1647 if (!list_empty(&nh_grp->fib_list))
1648 return;
1649 mlxsw_sp_nexthop_group_destroy(mlxsw_sp, nh_grp);
1650}
1651
013b20f9
IS
1652static bool
1653mlxsw_sp_fib_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
1654{
1655 struct mlxsw_sp_nexthop_group *nh_group = fib_entry->nh_group;
1656
9aecce1c
IS
1657 if (fib_entry->params.tos)
1658 return false;
1659
013b20f9
IS
1660 switch (fib_entry->type) {
1661 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
1662 return !!nh_group->adj_index_valid;
1663 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
70ad3506 1664 return !!nh_group->nh_rif;
013b20f9
IS
1665 default:
1666 return false;
1667 }
1668}
1669
1670static void mlxsw_sp_fib_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
1671{
1672 fib_entry->offloaded = true;
1673
76610ebb 1674 switch (fib_entry->fib_node->fib->proto) {
013b20f9
IS
1675 case MLXSW_SP_L3_PROTO_IPV4:
1676 fib_info_offload_inc(fib_entry->nh_group->key.fi);
1677 break;
1678 case MLXSW_SP_L3_PROTO_IPV6:
1679 WARN_ON_ONCE(1);
1680 }
1681}
1682
1683static void
1684mlxsw_sp_fib_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
1685{
76610ebb 1686 switch (fib_entry->fib_node->fib->proto) {
013b20f9
IS
1687 case MLXSW_SP_L3_PROTO_IPV4:
1688 fib_info_offload_dec(fib_entry->nh_group->key.fi);
1689 break;
1690 case MLXSW_SP_L3_PROTO_IPV6:
1691 WARN_ON_ONCE(1);
1692 }
1693
1694 fib_entry->offloaded = false;
1695}
1696
1697static void
1698mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
1699 enum mlxsw_reg_ralue_op op, int err)
1700{
1701 switch (op) {
1702 case MLXSW_REG_RALUE_OP_WRITE_DELETE:
1703 if (!fib_entry->offloaded)
1704 return;
1705 return mlxsw_sp_fib_entry_offload_unset(fib_entry);
1706 case MLXSW_REG_RALUE_OP_WRITE_WRITE:
1707 if (err)
1708 return;
1709 if (mlxsw_sp_fib_entry_should_offload(fib_entry) &&
1710 !fib_entry->offloaded)
1711 mlxsw_sp_fib_entry_offload_set(fib_entry);
1712 else if (!mlxsw_sp_fib_entry_should_offload(fib_entry) &&
1713 fib_entry->offloaded)
1714 mlxsw_sp_fib_entry_offload_unset(fib_entry);
1715 return;
1716 default:
1717 return;
1718 }
1719}
1720
a7ff87ac
JP
1721static int mlxsw_sp_fib_entry_op4_remote(struct mlxsw_sp *mlxsw_sp,
1722 struct mlxsw_sp_fib_entry *fib_entry,
1723 enum mlxsw_reg_ralue_op op)
1724{
1725 char ralue_pl[MLXSW_REG_RALUE_LEN];
76610ebb 1726 struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib;
9aecce1c 1727 u32 *p_dip = (u32 *) fib_entry->fib_node->key.addr;
a7ff87ac
JP
1728 enum mlxsw_reg_ralue_trap_action trap_action;
1729 u16 trap_id = 0;
1730 u32 adjacency_index = 0;
1731 u16 ecmp_size = 0;
1732
1733 /* In case the nexthop group adjacency index is valid, use it
1734 * with provided ECMP size. Otherwise, setup trap and pass
1735 * traffic to kernel.
1736 */
4b411477 1737 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
a7ff87ac
JP
1738 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
1739 adjacency_index = fib_entry->nh_group->adj_index;
1740 ecmp_size = fib_entry->nh_group->ecmp_size;
1741 } else {
1742 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
1743 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
1744 }
1745
1a9234e6 1746 mlxsw_reg_ralue_pack4(ralue_pl,
76610ebb
IS
1747 (enum mlxsw_reg_ralxx_protocol) fib->proto, op,
1748 fib->vr->id, fib_entry->fib_node->key.prefix_len,
9aecce1c 1749 *p_dip);
a7ff87ac
JP
1750 mlxsw_reg_ralue_act_remote_pack(ralue_pl, trap_action, trap_id,
1751 adjacency_index, ecmp_size);
1752 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
1753}
1754
61c503f9
JP
1755static int mlxsw_sp_fib_entry_op4_local(struct mlxsw_sp *mlxsw_sp,
1756 struct mlxsw_sp_fib_entry *fib_entry,
1757 enum mlxsw_reg_ralue_op op)
1758{
b8399a1e 1759 struct mlxsw_sp_rif *r = fib_entry->nh_group->nh_rif;
76610ebb 1760 struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib;
70ad3506 1761 enum mlxsw_reg_ralue_trap_action trap_action;
61c503f9 1762 char ralue_pl[MLXSW_REG_RALUE_LEN];
9aecce1c 1763 u32 *p_dip = (u32 *) fib_entry->fib_node->key.addr;
70ad3506
IS
1764 u16 trap_id = 0;
1765 u16 rif = 0;
1766
1767 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
1768 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
1769 rif = r->rif;
1770 } else {
1771 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
1772 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
1773 }
61c503f9 1774
1a9234e6 1775 mlxsw_reg_ralue_pack4(ralue_pl,
76610ebb
IS
1776 (enum mlxsw_reg_ralxx_protocol) fib->proto, op,
1777 fib->vr->id, fib_entry->fib_node->key.prefix_len,
9aecce1c 1778 *p_dip);
70ad3506 1779 mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, trap_id, rif);
61c503f9
JP
1780 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
1781}
1782
1783static int mlxsw_sp_fib_entry_op4_trap(struct mlxsw_sp *mlxsw_sp,
1784 struct mlxsw_sp_fib_entry *fib_entry,
1785 enum mlxsw_reg_ralue_op op)
1786{
76610ebb 1787 struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib;
61c503f9 1788 char ralue_pl[MLXSW_REG_RALUE_LEN];
9aecce1c 1789 u32 *p_dip = (u32 *) fib_entry->fib_node->key.addr;
61c503f9 1790
1a9234e6 1791 mlxsw_reg_ralue_pack4(ralue_pl,
76610ebb
IS
1792 (enum mlxsw_reg_ralxx_protocol) fib->proto, op,
1793 fib->vr->id, fib_entry->fib_node->key.prefix_len,
9aecce1c 1794 *p_dip);
61c503f9
JP
1795 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
1796 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
1797}
1798
1799static int mlxsw_sp_fib_entry_op4(struct mlxsw_sp *mlxsw_sp,
1800 struct mlxsw_sp_fib_entry *fib_entry,
1801 enum mlxsw_reg_ralue_op op)
1802{
1803 switch (fib_entry->type) {
1804 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
a7ff87ac 1805 return mlxsw_sp_fib_entry_op4_remote(mlxsw_sp, fib_entry, op);
61c503f9
JP
1806 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
1807 return mlxsw_sp_fib_entry_op4_local(mlxsw_sp, fib_entry, op);
1808 case MLXSW_SP_FIB_ENTRY_TYPE_TRAP:
1809 return mlxsw_sp_fib_entry_op4_trap(mlxsw_sp, fib_entry, op);
1810 }
1811 return -EINVAL;
1812}
1813
1814static int mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
1815 struct mlxsw_sp_fib_entry *fib_entry,
1816 enum mlxsw_reg_ralue_op op)
1817{
013b20f9
IS
1818 int err = -EINVAL;
1819
76610ebb 1820 switch (fib_entry->fib_node->fib->proto) {
61c503f9 1821 case MLXSW_SP_L3_PROTO_IPV4:
013b20f9
IS
1822 err = mlxsw_sp_fib_entry_op4(mlxsw_sp, fib_entry, op);
1823 break;
61c503f9 1824 case MLXSW_SP_L3_PROTO_IPV6:
013b20f9 1825 return err;
61c503f9 1826 }
013b20f9
IS
1827 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, err);
1828 return err;
61c503f9
JP
1829}
1830
1831static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
1832 struct mlxsw_sp_fib_entry *fib_entry)
1833{
7146da31
JP
1834 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
1835 MLXSW_REG_RALUE_OP_WRITE_WRITE);
61c503f9
JP
1836}
1837
1838static int mlxsw_sp_fib_entry_del(struct mlxsw_sp *mlxsw_sp,
1839 struct mlxsw_sp_fib_entry *fib_entry)
1840{
1841 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
1842 MLXSW_REG_RALUE_OP_WRITE_DELETE);
1843}
1844
61c503f9 1845static int
013b20f9
IS
1846mlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp,
1847 const struct fib_entry_notifier_info *fen_info,
1848 struct mlxsw_sp_fib_entry *fib_entry)
61c503f9 1849{
b45f64d1 1850 struct fib_info *fi = fen_info->fi;
61c503f9 1851
97989ee0
IS
1852 switch (fen_info->type) {
1853 case RTN_BROADCAST: /* fall through */
1854 case RTN_LOCAL:
61c503f9
JP
1855 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
1856 return 0;
97989ee0
IS
1857 case RTN_UNREACHABLE: /* fall through */
1858 case RTN_BLACKHOLE: /* fall through */
1859 case RTN_PROHIBIT:
1860 /* Packets hitting these routes need to be trapped, but
1861 * can do so with a lower priority than packets directed
1862 * at the host, so use action type local instead of trap.
1863 */
61c503f9 1864 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
97989ee0
IS
1865 return 0;
1866 case RTN_UNICAST:
1867 if (fi->fib_nh->nh_scope != RT_SCOPE_LINK)
1868 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
1869 else
1870 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
1871 return 0;
1872 default:
1873 return -EINVAL;
1874 }
a7ff87ac
JP
1875}
1876
5b004412 1877static struct mlxsw_sp_fib_entry *
9aecce1c
IS
1878mlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp,
1879 struct mlxsw_sp_fib_node *fib_node,
1880 const struct fib_entry_notifier_info *fen_info)
61c503f9 1881{
61c503f9 1882 struct mlxsw_sp_fib_entry *fib_entry;
61c503f9
JP
1883 int err;
1884
9aecce1c 1885 fib_entry = kzalloc(sizeof(*fib_entry), GFP_KERNEL);
61c503f9
JP
1886 if (!fib_entry) {
1887 err = -ENOMEM;
9aecce1c 1888 goto err_fib_entry_alloc;
61c503f9 1889 }
61c503f9 1890
013b20f9 1891 err = mlxsw_sp_fib4_entry_type_set(mlxsw_sp, fen_info, fib_entry);
61c503f9 1892 if (err)
013b20f9 1893 goto err_fib4_entry_type_set;
61c503f9 1894
9aecce1c 1895 err = mlxsw_sp_nexthop_group_get(mlxsw_sp, fib_entry, fen_info->fi);
b8399a1e
IS
1896 if (err)
1897 goto err_nexthop_group_get;
1898
9aecce1c
IS
1899 fib_entry->params.prio = fen_info->fi->fib_priority;
1900 fib_entry->params.tb_id = fen_info->tb_id;
1901 fib_entry->params.type = fen_info->type;
1902 fib_entry->params.tos = fen_info->tos;
1903
1904 fib_entry->fib_node = fib_node;
1905
5b004412
JP
1906 return fib_entry;
1907
b8399a1e 1908err_nexthop_group_get:
013b20f9 1909err_fib4_entry_type_set:
9aecce1c
IS
1910 kfree(fib_entry);
1911err_fib_entry_alloc:
5b004412
JP
1912 return ERR_PTR(err);
1913}
1914
9aecce1c
IS
1915static void mlxsw_sp_fib4_entry_destroy(struct mlxsw_sp *mlxsw_sp,
1916 struct mlxsw_sp_fib_entry *fib_entry)
1917{
1918 mlxsw_sp_nexthop_group_put(mlxsw_sp, fib_entry);
1919 kfree(fib_entry);
1920}
1921
1922static struct mlxsw_sp_fib_node *
1923mlxsw_sp_fib4_node_get(struct mlxsw_sp *mlxsw_sp,
1924 const struct fib_entry_notifier_info *fen_info);
1925
5b004412 1926static struct mlxsw_sp_fib_entry *
9aecce1c
IS
1927mlxsw_sp_fib4_entry_lookup(struct mlxsw_sp *mlxsw_sp,
1928 const struct fib_entry_notifier_info *fen_info)
5b004412 1929{
9aecce1c
IS
1930 struct mlxsw_sp_fib_entry *fib_entry;
1931 struct mlxsw_sp_fib_node *fib_node;
5b004412 1932
9aecce1c
IS
1933 fib_node = mlxsw_sp_fib4_node_get(mlxsw_sp, fen_info);
1934 if (IS_ERR(fib_node))
1935 return NULL;
1936
1937 list_for_each_entry(fib_entry, &fib_node->entry_list, list) {
1938 if (fib_entry->params.tb_id == fen_info->tb_id &&
1939 fib_entry->params.tos == fen_info->tos &&
1940 fib_entry->params.type == fen_info->type &&
1941 fib_entry->nh_group->key.fi == fen_info->fi) {
1942 return fib_entry;
1943 }
1944 }
1945
1946 return NULL;
1947}
1948
1949static const struct rhashtable_params mlxsw_sp_fib_ht_params = {
1950 .key_offset = offsetof(struct mlxsw_sp_fib_node, key),
1951 .head_offset = offsetof(struct mlxsw_sp_fib_node, ht_node),
1952 .key_len = sizeof(struct mlxsw_sp_fib_key),
1953 .automatic_shrinking = true,
1954};
1955
1956static int mlxsw_sp_fib_node_insert(struct mlxsw_sp_fib *fib,
1957 struct mlxsw_sp_fib_node *fib_node)
1958{
1959 return rhashtable_insert_fast(&fib->ht, &fib_node->ht_node,
1960 mlxsw_sp_fib_ht_params);
1961}
1962
1963static void mlxsw_sp_fib_node_remove(struct mlxsw_sp_fib *fib,
1964 struct mlxsw_sp_fib_node *fib_node)
1965{
1966 rhashtable_remove_fast(&fib->ht, &fib_node->ht_node,
1967 mlxsw_sp_fib_ht_params);
1968}
1969
1970static struct mlxsw_sp_fib_node *
1971mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
1972 size_t addr_len, unsigned char prefix_len)
1973{
1974 struct mlxsw_sp_fib_key key;
1975
1976 memset(&key, 0, sizeof(key));
1977 memcpy(key.addr, addr, addr_len);
1978 key.prefix_len = prefix_len;
1979 return rhashtable_lookup_fast(&fib->ht, &key, mlxsw_sp_fib_ht_params);
1980}
1981
1982static struct mlxsw_sp_fib_node *
76610ebb 1983mlxsw_sp_fib_node_create(struct mlxsw_sp_fib *fib, const void *addr,
9aecce1c
IS
1984 size_t addr_len, unsigned char prefix_len)
1985{
1986 struct mlxsw_sp_fib_node *fib_node;
1987
1988 fib_node = kzalloc(sizeof(*fib_node), GFP_KERNEL);
1989 if (!fib_node)
5b004412
JP
1990 return NULL;
1991
9aecce1c 1992 INIT_LIST_HEAD(&fib_node->entry_list);
76610ebb 1993 list_add(&fib_node->list, &fib->node_list);
9aecce1c
IS
1994 memcpy(fib_node->key.addr, addr, addr_len);
1995 fib_node->key.prefix_len = prefix_len;
9aecce1c
IS
1996
1997 return fib_node;
1998}
1999
2000static void mlxsw_sp_fib_node_destroy(struct mlxsw_sp_fib_node *fib_node)
2001{
9aecce1c
IS
2002 list_del(&fib_node->list);
2003 WARN_ON(!list_empty(&fib_node->entry_list));
2004 kfree(fib_node);
2005}
2006
2007static bool
2008mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
2009 const struct mlxsw_sp_fib_entry *fib_entry)
2010{
2011 return list_first_entry(&fib_node->entry_list,
2012 struct mlxsw_sp_fib_entry, list) == fib_entry;
2013}
2014
2015static void mlxsw_sp_fib_node_prefix_inc(struct mlxsw_sp_fib_node *fib_node)
2016{
2017 unsigned char prefix_len = fib_node->key.prefix_len;
76610ebb 2018 struct mlxsw_sp_fib *fib = fib_node->fib;
9aecce1c
IS
2019
2020 if (fib->prefix_ref_count[prefix_len]++ == 0)
2021 mlxsw_sp_prefix_usage_set(&fib->prefix_usage, prefix_len);
2022}
2023
2024static void mlxsw_sp_fib_node_prefix_dec(struct mlxsw_sp_fib_node *fib_node)
2025{
2026 unsigned char prefix_len = fib_node->key.prefix_len;
76610ebb 2027 struct mlxsw_sp_fib *fib = fib_node->fib;
9aecce1c
IS
2028
2029 if (--fib->prefix_ref_count[prefix_len] == 0)
2030 mlxsw_sp_prefix_usage_clear(&fib->prefix_usage, prefix_len);
5b004412
JP
2031}
2032
76610ebb
IS
2033static int mlxsw_sp_fib_node_init(struct mlxsw_sp *mlxsw_sp,
2034 struct mlxsw_sp_fib_node *fib_node,
2035 struct mlxsw_sp_fib *fib)
2036{
2037 struct mlxsw_sp_prefix_usage req_prefix_usage;
2038 struct mlxsw_sp_lpm_tree *lpm_tree;
2039 int err;
2040
2041 err = mlxsw_sp_fib_node_insert(fib, fib_node);
2042 if (err)
2043 return err;
2044 fib_node->fib = fib;
2045
2046 mlxsw_sp_prefix_usage_cpy(&req_prefix_usage, &fib->prefix_usage);
2047 mlxsw_sp_prefix_usage_set(&req_prefix_usage, fib_node->key.prefix_len);
2048
2049 if (!mlxsw_sp_prefix_usage_none(&fib->prefix_usage)) {
2050 err = mlxsw_sp_vr_lpm_tree_check(mlxsw_sp, fib,
2051 &req_prefix_usage);
2052 if (err)
2053 goto err_tree_check;
2054 } else {
2055 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
2056 fib->proto);
2057 if (IS_ERR(lpm_tree))
2058 return PTR_ERR(lpm_tree);
2059 fib->lpm_tree = lpm_tree;
2060 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib);
2061 if (err)
2062 goto err_tree_bind;
2063 }
2064
2065 mlxsw_sp_fib_node_prefix_inc(fib_node);
2066
2067 return 0;
2068
2069err_tree_bind:
2070 fib->lpm_tree = NULL;
2071 mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree);
2072err_tree_check:
2073 fib_node->fib = NULL;
2074 mlxsw_sp_fib_node_remove(fib, fib_node);
2075 return err;
2076}
2077
2078static void mlxsw_sp_fib_node_fini(struct mlxsw_sp *mlxsw_sp,
2079 struct mlxsw_sp_fib_node *fib_node)
2080{
2081 struct mlxsw_sp_lpm_tree *lpm_tree = fib_node->fib->lpm_tree;
2082 struct mlxsw_sp_fib *fib = fib_node->fib;
2083
2084 mlxsw_sp_fib_node_prefix_dec(fib_node);
2085
2086 if (mlxsw_sp_prefix_usage_none(&fib->prefix_usage)) {
2087 mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, fib);
2088 fib->lpm_tree = NULL;
2089 mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree);
2090 } else {
2091 mlxsw_sp_vr_lpm_tree_check(mlxsw_sp, fib, &fib->prefix_usage);
2092 }
2093
2094 fib_node->fib = NULL;
2095 mlxsw_sp_fib_node_remove(fib, fib_node);
2096}
2097
9aecce1c
IS
2098static struct mlxsw_sp_fib_node *
2099mlxsw_sp_fib4_node_get(struct mlxsw_sp *mlxsw_sp,
2100 const struct fib_entry_notifier_info *fen_info)
5b004412 2101{
9aecce1c 2102 struct mlxsw_sp_fib_node *fib_node;
76610ebb 2103 struct mlxsw_sp_fib *fib;
9aecce1c
IS
2104 struct mlxsw_sp_vr *vr;
2105 int err;
2106
76610ebb 2107 vr = mlxsw_sp_vr_get(mlxsw_sp, fen_info->tb_id);
9aecce1c
IS
2108 if (IS_ERR(vr))
2109 return ERR_CAST(vr);
76610ebb 2110 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV4);
9aecce1c 2111
76610ebb 2112 fib_node = mlxsw_sp_fib_node_lookup(fib, &fen_info->dst,
9aecce1c
IS
2113 sizeof(fen_info->dst),
2114 fen_info->dst_len);
2115 if (fib_node)
2116 return fib_node;
5b004412 2117
76610ebb 2118 fib_node = mlxsw_sp_fib_node_create(fib, &fen_info->dst,
9aecce1c
IS
2119 sizeof(fen_info->dst),
2120 fen_info->dst_len);
2121 if (!fib_node) {
2122 err = -ENOMEM;
2123 goto err_fib_node_create;
5b004412 2124 }
9aecce1c 2125
76610ebb
IS
2126 err = mlxsw_sp_fib_node_init(mlxsw_sp, fib_node, fib);
2127 if (err)
2128 goto err_fib_node_init;
2129
9aecce1c
IS
2130 return fib_node;
2131
76610ebb
IS
2132err_fib_node_init:
2133 mlxsw_sp_fib_node_destroy(fib_node);
9aecce1c 2134err_fib_node_create:
76610ebb 2135 mlxsw_sp_vr_put(vr);
9aecce1c 2136 return ERR_PTR(err);
5b004412
JP
2137}
2138
9aecce1c
IS
2139static void mlxsw_sp_fib4_node_put(struct mlxsw_sp *mlxsw_sp,
2140 struct mlxsw_sp_fib_node *fib_node)
5b004412 2141{
76610ebb 2142 struct mlxsw_sp_vr *vr = fib_node->fib->vr;
5b004412 2143
9aecce1c
IS
2144 if (!list_empty(&fib_node->entry_list))
2145 return;
76610ebb 2146 mlxsw_sp_fib_node_fini(mlxsw_sp, fib_node);
9aecce1c 2147 mlxsw_sp_fib_node_destroy(fib_node);
76610ebb 2148 mlxsw_sp_vr_put(vr);
61c503f9
JP
2149}
2150
9aecce1c
IS
2151static struct mlxsw_sp_fib_entry *
2152mlxsw_sp_fib4_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
2153 const struct mlxsw_sp_fib_entry_params *params)
61c503f9 2154{
61c503f9 2155 struct mlxsw_sp_fib_entry *fib_entry;
9aecce1c
IS
2156
2157 list_for_each_entry(fib_entry, &fib_node->entry_list, list) {
2158 if (fib_entry->params.tb_id > params->tb_id)
2159 continue;
2160 if (fib_entry->params.tb_id != params->tb_id)
2161 break;
2162 if (fib_entry->params.tos > params->tos)
2163 continue;
2164 if (fib_entry->params.prio >= params->prio ||
2165 fib_entry->params.tos < params->tos)
2166 return fib_entry;
2167 }
2168
2169 return NULL;
2170}
2171
4283bce5
IS
2172static int mlxsw_sp_fib4_node_list_append(struct mlxsw_sp_fib_entry *fib_entry,
2173 struct mlxsw_sp_fib_entry *new_entry)
2174{
2175 struct mlxsw_sp_fib_node *fib_node;
2176
2177 if (WARN_ON(!fib_entry))
2178 return -EINVAL;
2179
2180 fib_node = fib_entry->fib_node;
2181 list_for_each_entry_from(fib_entry, &fib_node->entry_list, list) {
2182 if (fib_entry->params.tb_id != new_entry->params.tb_id ||
2183 fib_entry->params.tos != new_entry->params.tos ||
2184 fib_entry->params.prio != new_entry->params.prio)
2185 break;
2186 }
2187
2188 list_add_tail(&new_entry->list, &fib_entry->list);
2189 return 0;
2190}
2191
9aecce1c
IS
2192static int
2193mlxsw_sp_fib4_node_list_insert(struct mlxsw_sp_fib_node *fib_node,
4283bce5 2194 struct mlxsw_sp_fib_entry *new_entry,
599cf8f9 2195 bool replace, bool append)
9aecce1c
IS
2196{
2197 struct mlxsw_sp_fib_entry *fib_entry;
2198
2199 fib_entry = mlxsw_sp_fib4_node_entry_find(fib_node, &new_entry->params);
2200
4283bce5
IS
2201 if (append)
2202 return mlxsw_sp_fib4_node_list_append(fib_entry, new_entry);
599cf8f9
IS
2203 if (replace && WARN_ON(!fib_entry))
2204 return -EINVAL;
4283bce5 2205
599cf8f9
IS
2206 /* Insert new entry before replaced one, so that we can later
2207 * remove the second.
2208 */
9aecce1c
IS
2209 if (fib_entry) {
2210 list_add_tail(&new_entry->list, &fib_entry->list);
2211 } else {
2212 struct mlxsw_sp_fib_entry *last;
2213
2214 list_for_each_entry(last, &fib_node->entry_list, list) {
2215 if (new_entry->params.tb_id > last->params.tb_id)
2216 break;
2217 fib_entry = last;
2218 }
2219
2220 if (fib_entry)
2221 list_add(&new_entry->list, &fib_entry->list);
2222 else
2223 list_add(&new_entry->list, &fib_node->entry_list);
2224 }
2225
2226 return 0;
2227}
2228
2229static void
2230mlxsw_sp_fib4_node_list_remove(struct mlxsw_sp_fib_entry *fib_entry)
2231{
2232 list_del(&fib_entry->list);
2233}
2234
2235static int
2236mlxsw_sp_fib4_node_entry_add(struct mlxsw_sp *mlxsw_sp,
2237 const struct mlxsw_sp_fib_node *fib_node,
2238 struct mlxsw_sp_fib_entry *fib_entry)
2239{
2240 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
2241 return 0;
2242
2243 /* To prevent packet loss, overwrite the previously offloaded
2244 * entry.
2245 */
2246 if (!list_is_singular(&fib_node->entry_list)) {
2247 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
2248 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
2249
2250 mlxsw_sp_fib_entry_offload_refresh(n, op, 0);
2251 }
2252
2253 return mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
2254}
2255
2256static void
2257mlxsw_sp_fib4_node_entry_del(struct mlxsw_sp *mlxsw_sp,
2258 const struct mlxsw_sp_fib_node *fib_node,
2259 struct mlxsw_sp_fib_entry *fib_entry)
2260{
2261 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
2262 return;
2263
2264 /* Promote the next entry by overwriting the deleted entry */
2265 if (!list_is_singular(&fib_node->entry_list)) {
2266 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
2267 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
2268
2269 mlxsw_sp_fib_entry_update(mlxsw_sp, n);
2270 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
2271 return;
2272 }
2273
2274 mlxsw_sp_fib_entry_del(mlxsw_sp, fib_entry);
2275}
2276
2277static int mlxsw_sp_fib4_node_entry_link(struct mlxsw_sp *mlxsw_sp,
4283bce5 2278 struct mlxsw_sp_fib_entry *fib_entry,
599cf8f9 2279 bool replace, bool append)
9aecce1c
IS
2280{
2281 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
2282 int err;
2283
599cf8f9
IS
2284 err = mlxsw_sp_fib4_node_list_insert(fib_node, fib_entry, replace,
2285 append);
9aecce1c
IS
2286 if (err)
2287 return err;
2288
2289 err = mlxsw_sp_fib4_node_entry_add(mlxsw_sp, fib_node, fib_entry);
2290 if (err)
2291 goto err_fib4_node_entry_add;
2292
9aecce1c
IS
2293 return 0;
2294
2295err_fib4_node_entry_add:
2296 mlxsw_sp_fib4_node_list_remove(fib_entry);
2297 return err;
2298}
2299
2300static void
2301mlxsw_sp_fib4_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
2302 struct mlxsw_sp_fib_entry *fib_entry)
2303{
2304 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
2305
9aecce1c
IS
2306 mlxsw_sp_fib4_node_entry_del(mlxsw_sp, fib_node, fib_entry);
2307 mlxsw_sp_fib4_node_list_remove(fib_entry);
2308}
2309
599cf8f9
IS
2310static void mlxsw_sp_fib4_entry_replace(struct mlxsw_sp *mlxsw_sp,
2311 struct mlxsw_sp_fib_entry *fib_entry,
2312 bool replace)
2313{
2314 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
2315 struct mlxsw_sp_fib_entry *replaced;
2316
2317 if (!replace)
2318 return;
2319
2320 /* We inserted the new entry before replaced one */
2321 replaced = list_next_entry(fib_entry, list);
2322
2323 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, replaced);
2324 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, replaced);
2325 mlxsw_sp_fib4_node_put(mlxsw_sp, fib_node);
2326}
2327
9aecce1c
IS
2328static int
2329mlxsw_sp_router_fib4_add(struct mlxsw_sp *mlxsw_sp,
4283bce5 2330 const struct fib_entry_notifier_info *fen_info,
599cf8f9 2331 bool replace, bool append)
9aecce1c
IS
2332{
2333 struct mlxsw_sp_fib_entry *fib_entry;
2334 struct mlxsw_sp_fib_node *fib_node;
61c503f9
JP
2335 int err;
2336
b45f64d1
JP
2337 if (mlxsw_sp->router.aborted)
2338 return 0;
2339
9aecce1c
IS
2340 fib_node = mlxsw_sp_fib4_node_get(mlxsw_sp, fen_info);
2341 if (IS_ERR(fib_node)) {
2342 dev_warn(mlxsw_sp->bus_info->dev, "Failed to get FIB node\n");
2343 return PTR_ERR(fib_node);
b45f64d1 2344 }
61c503f9 2345
9aecce1c
IS
2346 fib_entry = mlxsw_sp_fib4_entry_create(mlxsw_sp, fib_node, fen_info);
2347 if (IS_ERR(fib_entry)) {
2348 dev_warn(mlxsw_sp->bus_info->dev, "Failed to create FIB entry\n");
2349 err = PTR_ERR(fib_entry);
2350 goto err_fib4_entry_create;
2351 }
5b004412 2352
599cf8f9
IS
2353 err = mlxsw_sp_fib4_node_entry_link(mlxsw_sp, fib_entry, replace,
2354 append);
b45f64d1 2355 if (err) {
9aecce1c
IS
2356 dev_warn(mlxsw_sp->bus_info->dev, "Failed to link FIB entry to node\n");
2357 goto err_fib4_node_entry_link;
b45f64d1 2358 }
9aecce1c 2359
599cf8f9
IS
2360 mlxsw_sp_fib4_entry_replace(mlxsw_sp, fib_entry, replace);
2361
61c503f9
JP
2362 return 0;
2363
9aecce1c
IS
2364err_fib4_node_entry_link:
2365 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib_entry);
2366err_fib4_entry_create:
2367 mlxsw_sp_fib4_node_put(mlxsw_sp, fib_node);
61c503f9
JP
2368 return err;
2369}
2370
37956d78
JP
2371static void mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp,
2372 struct fib_entry_notifier_info *fen_info)
61c503f9 2373{
61c503f9 2374 struct mlxsw_sp_fib_entry *fib_entry;
9aecce1c 2375 struct mlxsw_sp_fib_node *fib_node;
61c503f9 2376
b45f64d1 2377 if (mlxsw_sp->router.aborted)
37956d78 2378 return;
b45f64d1 2379
9aecce1c
IS
2380 fib_entry = mlxsw_sp_fib4_entry_lookup(mlxsw_sp, fen_info);
2381 if (WARN_ON(!fib_entry))
37956d78 2382 return;
9aecce1c 2383 fib_node = fib_entry->fib_node;
5b004412 2384
9aecce1c
IS
2385 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib_entry);
2386 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib_entry);
2387 mlxsw_sp_fib4_node_put(mlxsw_sp, fib_node);
61c503f9 2388}
b45f64d1
JP
2389
2390static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp)
2391{
2392 char ralta_pl[MLXSW_REG_RALTA_LEN];
2393 char ralst_pl[MLXSW_REG_RALST_LEN];
2394 char raltb_pl[MLXSW_REG_RALTB_LEN];
2395 char ralue_pl[MLXSW_REG_RALUE_LEN];
2396 int err;
2397
2398 mlxsw_reg_ralta_pack(ralta_pl, true, MLXSW_REG_RALXX_PROTOCOL_IPV4,
2399 MLXSW_SP_LPM_TREE_MIN);
2400 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
2401 if (err)
2402 return err;
2403
2404 mlxsw_reg_ralst_pack(ralst_pl, 0xff, MLXSW_SP_LPM_TREE_MIN);
2405 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
2406 if (err)
2407 return err;
2408
19271c1a
JP
2409 mlxsw_reg_raltb_pack(raltb_pl, 0, MLXSW_REG_RALXX_PROTOCOL_IPV4,
2410 MLXSW_SP_LPM_TREE_MIN);
b45f64d1
JP
2411 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
2412 if (err)
2413 return err;
2414
2415 mlxsw_reg_ralue_pack4(ralue_pl, MLXSW_SP_L3_PROTO_IPV4,
2416 MLXSW_REG_RALUE_OP_WRITE_WRITE, 0, 0, 0);
2417 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
2418 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2419}
2420
9aecce1c
IS
2421static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp,
2422 struct mlxsw_sp_fib_node *fib_node)
2423{
2424 struct mlxsw_sp_fib_entry *fib_entry, *tmp;
2425
2426 list_for_each_entry_safe(fib_entry, tmp, &fib_node->entry_list, list) {
2427 bool do_break = &tmp->list == &fib_node->entry_list;
2428
2429 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib_entry);
2430 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib_entry);
2431 mlxsw_sp_fib4_node_put(mlxsw_sp, fib_node);
2432 /* Break when entry list is empty and node was freed.
2433 * Otherwise, we'll access freed memory in the next
2434 * iteration.
2435 */
2436 if (do_break)
2437 break;
2438 }
2439}
2440
2441static void mlxsw_sp_fib_node_flush(struct mlxsw_sp *mlxsw_sp,
2442 struct mlxsw_sp_fib_node *fib_node)
2443{
76610ebb 2444 switch (fib_node->fib->proto) {
9aecce1c
IS
2445 case MLXSW_SP_L3_PROTO_IPV4:
2446 mlxsw_sp_fib4_node_flush(mlxsw_sp, fib_node);
2447 break;
2448 case MLXSW_SP_L3_PROTO_IPV6:
2449 WARN_ON_ONCE(1);
2450 break;
2451 }
2452}
2453
76610ebb
IS
2454static void mlxsw_sp_vr_fib_flush(struct mlxsw_sp *mlxsw_sp,
2455 struct mlxsw_sp_vr *vr,
2456 enum mlxsw_sp_l3proto proto)
b45f64d1 2457{
76610ebb 2458 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
9aecce1c 2459 struct mlxsw_sp_fib_node *fib_node, *tmp;
76610ebb
IS
2460
2461 list_for_each_entry_safe(fib_node, tmp, &fib->node_list, list) {
2462 bool do_break = &tmp->list == &fib->node_list;
2463
2464 mlxsw_sp_fib_node_flush(mlxsw_sp, fib_node);
2465 if (do_break)
2466 break;
2467 }
2468}
2469
2470static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp)
2471{
b45f64d1 2472 int i;
b45f64d1 2473
c1a38311 2474 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
76610ebb 2475 struct mlxsw_sp_vr *vr = &mlxsw_sp->router.vrs[i];
ac571de9 2476
76610ebb 2477 if (!mlxsw_sp_vr_is_used(vr))
b45f64d1 2478 continue;
76610ebb 2479 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV4);
b45f64d1 2480 }
ac571de9
IS
2481}
2482
2483static void mlxsw_sp_router_fib4_abort(struct mlxsw_sp *mlxsw_sp)
2484{
2485 int err;
2486
d331d303
IS
2487 if (mlxsw_sp->router.aborted)
2488 return;
2489 dev_warn(mlxsw_sp->bus_info->dev, "FIB abort triggered. Note that FIB entries are no longer being offloaded to this device.\n");
ac571de9 2490 mlxsw_sp_router_fib_flush(mlxsw_sp);
b45f64d1
JP
2491 mlxsw_sp->router.aborted = true;
2492 err = mlxsw_sp_router_set_abort_trap(mlxsw_sp);
2493 if (err)
2494 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set abort trap.\n");
2495}
2496
3057224e 2497struct mlxsw_sp_fib_event_work {
a0e4761d 2498 struct work_struct work;
ad178c8e
IS
2499 union {
2500 struct fib_entry_notifier_info fen_info;
2501 struct fib_nh_notifier_info fnh_info;
2502 };
3057224e
IS
2503 struct mlxsw_sp *mlxsw_sp;
2504 unsigned long event;
2505};
2506
2507static void mlxsw_sp_router_fib_event_work(struct work_struct *work)
b45f64d1 2508{
3057224e 2509 struct mlxsw_sp_fib_event_work *fib_work =
a0e4761d 2510 container_of(work, struct mlxsw_sp_fib_event_work, work);
3057224e 2511 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
599cf8f9 2512 bool replace, append;
b45f64d1
JP
2513 int err;
2514
3057224e
IS
2515 /* Protect internal structures from changes */
2516 rtnl_lock();
2517 switch (fib_work->event) {
599cf8f9 2518 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
4283bce5 2519 case FIB_EVENT_ENTRY_APPEND: /* fall through */
b45f64d1 2520 case FIB_EVENT_ENTRY_ADD:
599cf8f9 2521 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
4283bce5
IS
2522 append = fib_work->event == FIB_EVENT_ENTRY_APPEND;
2523 err = mlxsw_sp_router_fib4_add(mlxsw_sp, &fib_work->fen_info,
599cf8f9 2524 replace, append);
b45f64d1
JP
2525 if (err)
2526 mlxsw_sp_router_fib4_abort(mlxsw_sp);
3057224e 2527 fib_info_put(fib_work->fen_info.fi);
b45f64d1
JP
2528 break;
2529 case FIB_EVENT_ENTRY_DEL:
3057224e
IS
2530 mlxsw_sp_router_fib4_del(mlxsw_sp, &fib_work->fen_info);
2531 fib_info_put(fib_work->fen_info.fi);
b45f64d1
JP
2532 break;
2533 case FIB_EVENT_RULE_ADD: /* fall through */
2534 case FIB_EVENT_RULE_DEL:
2535 mlxsw_sp_router_fib4_abort(mlxsw_sp);
2536 break;
ad178c8e
IS
2537 case FIB_EVENT_NH_ADD: /* fall through */
2538 case FIB_EVENT_NH_DEL:
2539 mlxsw_sp_nexthop_event(mlxsw_sp, fib_work->event,
2540 fib_work->fnh_info.fib_nh);
2541 fib_info_put(fib_work->fnh_info.fib_nh->nh_parent);
2542 break;
b45f64d1 2543 }
3057224e
IS
2544 rtnl_unlock();
2545 kfree(fib_work);
2546}
2547
2548/* Called with rcu_read_lock() */
2549static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
2550 unsigned long event, void *ptr)
2551{
2552 struct mlxsw_sp *mlxsw_sp = container_of(nb, struct mlxsw_sp, fib_nb);
2553 struct mlxsw_sp_fib_event_work *fib_work;
2554 struct fib_notifier_info *info = ptr;
2555
2556 if (!net_eq(info->net, &init_net))
2557 return NOTIFY_DONE;
2558
2559 fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC);
2560 if (WARN_ON(!fib_work))
2561 return NOTIFY_BAD;
2562
a0e4761d 2563 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib_event_work);
3057224e
IS
2564 fib_work->mlxsw_sp = mlxsw_sp;
2565 fib_work->event = event;
2566
2567 switch (event) {
599cf8f9 2568 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
4283bce5 2569 case FIB_EVENT_ENTRY_APPEND: /* fall through */
3057224e
IS
2570 case FIB_EVENT_ENTRY_ADD: /* fall through */
2571 case FIB_EVENT_ENTRY_DEL:
2572 memcpy(&fib_work->fen_info, ptr, sizeof(fib_work->fen_info));
2573 /* Take referece on fib_info to prevent it from being
2574 * freed while work is queued. Release it afterwards.
2575 */
2576 fib_info_hold(fib_work->fen_info.fi);
2577 break;
ad178c8e
IS
2578 case FIB_EVENT_NH_ADD: /* fall through */
2579 case FIB_EVENT_NH_DEL:
2580 memcpy(&fib_work->fnh_info, ptr, sizeof(fib_work->fnh_info));
2581 fib_info_hold(fib_work->fnh_info.fib_nh->nh_parent);
2582 break;
3057224e
IS
2583 }
2584
a0e4761d 2585 mlxsw_core_schedule_work(&fib_work->work);
3057224e 2586
b45f64d1
JP
2587 return NOTIFY_DONE;
2588}
2589
4724ba56
IS
2590static struct mlxsw_sp_rif *
2591mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
2592 const struct net_device *dev)
2593{
2594 int i;
2595
2596 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
2597 if (mlxsw_sp->rifs[i] && mlxsw_sp->rifs[i]->dev == dev)
2598 return mlxsw_sp->rifs[i];
2599
2600 return NULL;
2601}
2602
2603static int mlxsw_sp_router_rif_disable(struct mlxsw_sp *mlxsw_sp, u16 rif)
2604{
2605 char ritr_pl[MLXSW_REG_RITR_LEN];
2606 int err;
2607
2608 mlxsw_reg_ritr_rif_pack(ritr_pl, rif);
2609 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
2610 if (WARN_ON_ONCE(err))
2611 return err;
2612
2613 mlxsw_reg_ritr_enable_set(ritr_pl, false);
2614 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
2615}
2616
2617static void mlxsw_sp_router_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
2618 struct mlxsw_sp_rif *r)
2619{
2620 mlxsw_sp_router_rif_disable(mlxsw_sp, r->rif);
2621 mlxsw_sp_nexthop_rif_gone_sync(mlxsw_sp, r);
2622 mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, r);
2623}
2624
2625static bool mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *r,
2626 const struct in_device *in_dev,
2627 unsigned long event)
2628{
2629 switch (event) {
2630 case NETDEV_UP:
2631 if (!r)
2632 return true;
2633 return false;
2634 case NETDEV_DOWN:
2635 if (r && !in_dev->ifa_list)
2636 return true;
2637 /* It is possible we already removed the RIF ourselves
2638 * if it was assigned to a netdev that is now a bridge
2639 * or LAG slave.
2640 */
2641 return false;
2642 }
2643
2644 return false;
2645}
2646
2647#define MLXSW_SP_INVALID_RIF 0xffff
2648static int mlxsw_sp_avail_rif_get(struct mlxsw_sp *mlxsw_sp)
2649{
2650 int i;
2651
2652 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
2653 if (!mlxsw_sp->rifs[i])
2654 return i;
2655
2656 return MLXSW_SP_INVALID_RIF;
2657}
2658
2659static void mlxsw_sp_vport_rif_sp_attr_get(struct mlxsw_sp_port *mlxsw_sp_vport,
2660 bool *p_lagged, u16 *p_system_port)
2661{
2662 u8 local_port = mlxsw_sp_vport->local_port;
2663
2664 *p_lagged = mlxsw_sp_vport->lagged;
2665 *p_system_port = *p_lagged ? mlxsw_sp_vport->lag_id : local_port;
2666}
2667
2668static int mlxsw_sp_vport_rif_sp_op(struct mlxsw_sp_port *mlxsw_sp_vport,
2669 struct net_device *l3_dev, u16 rif,
2670 bool create)
2671{
2672 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp;
2673 bool lagged = mlxsw_sp_vport->lagged;
2674 char ritr_pl[MLXSW_REG_RITR_LEN];
2675 u16 system_port;
2676
2677 mlxsw_reg_ritr_pack(ritr_pl, create, MLXSW_REG_RITR_SP_IF, rif,
2678 l3_dev->mtu, l3_dev->dev_addr);
2679
2680 mlxsw_sp_vport_rif_sp_attr_get(mlxsw_sp_vport, &lagged, &system_port);
2681 mlxsw_reg_ritr_sp_if_pack(ritr_pl, lagged, system_port,
2682 mlxsw_sp_vport_vid_get(mlxsw_sp_vport));
2683
2684 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
2685}
2686
2687static void mlxsw_sp_vport_rif_sp_leave(struct mlxsw_sp_port *mlxsw_sp_vport);
2688
2689static u16 mlxsw_sp_rif_sp_to_fid(u16 rif)
2690{
2691 return MLXSW_SP_RFID_BASE + rif;
2692}
2693
2694static struct mlxsw_sp_fid *
2695mlxsw_sp_rfid_alloc(u16 fid, struct net_device *l3_dev)
2696{
2697 struct mlxsw_sp_fid *f;
2698
2699 f = kzalloc(sizeof(*f), GFP_KERNEL);
2700 if (!f)
2701 return NULL;
2702
2703 f->leave = mlxsw_sp_vport_rif_sp_leave;
2704 f->ref_count = 0;
2705 f->dev = l3_dev;
2706 f->fid = fid;
2707
2708 return f;
2709}
2710
2711static struct mlxsw_sp_rif *
2712mlxsw_sp_rif_alloc(u16 rif, struct net_device *l3_dev, struct mlxsw_sp_fid *f)
2713{
2714 struct mlxsw_sp_rif *r;
2715
2716 r = kzalloc(sizeof(*r), GFP_KERNEL);
2717 if (!r)
2718 return NULL;
2719
2720 INIT_LIST_HEAD(&r->nexthop_list);
2721 INIT_LIST_HEAD(&r->neigh_list);
2722 ether_addr_copy(r->addr, l3_dev->dev_addr);
2723 r->mtu = l3_dev->mtu;
2724 r->dev = l3_dev;
2725 r->rif = rif;
2726 r->f = f;
2727
2728 return r;
2729}
2730
2731static struct mlxsw_sp_rif *
2732mlxsw_sp_vport_rif_sp_create(struct mlxsw_sp_port *mlxsw_sp_vport,
2733 struct net_device *l3_dev)
2734{
2735 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp;
2736 struct mlxsw_sp_fid *f;
2737 struct mlxsw_sp_rif *r;
2738 u16 fid, rif;
2739 int err;
2740
2741 rif = mlxsw_sp_avail_rif_get(mlxsw_sp);
2742 if (rif == MLXSW_SP_INVALID_RIF)
2743 return ERR_PTR(-ERANGE);
2744
2745 err = mlxsw_sp_vport_rif_sp_op(mlxsw_sp_vport, l3_dev, rif, true);
2746 if (err)
2747 return ERR_PTR(err);
2748
2749 fid = mlxsw_sp_rif_sp_to_fid(rif);
2750 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, fid, true);
2751 if (err)
2752 goto err_rif_fdb_op;
2753
2754 f = mlxsw_sp_rfid_alloc(fid, l3_dev);
2755 if (!f) {
2756 err = -ENOMEM;
2757 goto err_rfid_alloc;
2758 }
2759
2760 r = mlxsw_sp_rif_alloc(rif, l3_dev, f);
2761 if (!r) {
2762 err = -ENOMEM;
2763 goto err_rif_alloc;
2764 }
2765
2766 f->r = r;
2767 mlxsw_sp->rifs[rif] = r;
2768
2769 return r;
2770
2771err_rif_alloc:
2772 kfree(f);
2773err_rfid_alloc:
2774 mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, fid, false);
2775err_rif_fdb_op:
2776 mlxsw_sp_vport_rif_sp_op(mlxsw_sp_vport, l3_dev, rif, false);
2777 return ERR_PTR(err);
2778}
2779
2780static void mlxsw_sp_vport_rif_sp_destroy(struct mlxsw_sp_port *mlxsw_sp_vport,
2781 struct mlxsw_sp_rif *r)
2782{
2783 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp;
2784 struct net_device *l3_dev = r->dev;
2785 struct mlxsw_sp_fid *f = r->f;
2786 u16 fid = f->fid;
2787 u16 rif = r->rif;
2788
2789 mlxsw_sp_router_rif_gone_sync(mlxsw_sp, r);
2790
2791 mlxsw_sp->rifs[rif] = NULL;
2792 f->r = NULL;
2793
2794 kfree(r);
2795
2796 kfree(f);
2797
2798 mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, fid, false);
2799
2800 mlxsw_sp_vport_rif_sp_op(mlxsw_sp_vport, l3_dev, rif, false);
2801}
2802
2803static int mlxsw_sp_vport_rif_sp_join(struct mlxsw_sp_port *mlxsw_sp_vport,
2804 struct net_device *l3_dev)
2805{
2806 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp;
2807 struct mlxsw_sp_rif *r;
2808
2809 r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
2810 if (!r) {
2811 r = mlxsw_sp_vport_rif_sp_create(mlxsw_sp_vport, l3_dev);
2812 if (IS_ERR(r))
2813 return PTR_ERR(r);
2814 }
2815
2816 mlxsw_sp_vport_fid_set(mlxsw_sp_vport, r->f);
2817 r->f->ref_count++;
2818
2819 netdev_dbg(mlxsw_sp_vport->dev, "Joined FID=%d\n", r->f->fid);
2820
2821 return 0;
2822}
2823
2824static void mlxsw_sp_vport_rif_sp_leave(struct mlxsw_sp_port *mlxsw_sp_vport)
2825{
2826 struct mlxsw_sp_fid *f = mlxsw_sp_vport_fid_get(mlxsw_sp_vport);
2827
2828 netdev_dbg(mlxsw_sp_vport->dev, "Left FID=%d\n", f->fid);
2829
2830 mlxsw_sp_vport_fid_set(mlxsw_sp_vport, NULL);
2831 if (--f->ref_count == 0)
2832 mlxsw_sp_vport_rif_sp_destroy(mlxsw_sp_vport, f->r);
2833}
2834
2835static int mlxsw_sp_inetaddr_vport_event(struct net_device *l3_dev,
2836 struct net_device *port_dev,
2837 unsigned long event, u16 vid)
2838{
2839 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(port_dev);
2840 struct mlxsw_sp_port *mlxsw_sp_vport;
2841
2842 mlxsw_sp_vport = mlxsw_sp_port_vport_find(mlxsw_sp_port, vid);
2843 if (WARN_ON(!mlxsw_sp_vport))
2844 return -EINVAL;
2845
2846 switch (event) {
2847 case NETDEV_UP:
2848 return mlxsw_sp_vport_rif_sp_join(mlxsw_sp_vport, l3_dev);
2849 case NETDEV_DOWN:
2850 mlxsw_sp_vport_rif_sp_leave(mlxsw_sp_vport);
2851 break;
2852 }
2853
2854 return 0;
2855}
2856
2857static int mlxsw_sp_inetaddr_port_event(struct net_device *port_dev,
2858 unsigned long event)
2859{
2860 if (netif_is_bridge_port(port_dev) || netif_is_lag_port(port_dev))
2861 return 0;
2862
2863 return mlxsw_sp_inetaddr_vport_event(port_dev, port_dev, event, 1);
2864}
2865
2866static int __mlxsw_sp_inetaddr_lag_event(struct net_device *l3_dev,
2867 struct net_device *lag_dev,
2868 unsigned long event, u16 vid)
2869{
2870 struct net_device *port_dev;
2871 struct list_head *iter;
2872 int err;
2873
2874 netdev_for_each_lower_dev(lag_dev, port_dev, iter) {
2875 if (mlxsw_sp_port_dev_check(port_dev)) {
2876 err = mlxsw_sp_inetaddr_vport_event(l3_dev, port_dev,
2877 event, vid);
2878 if (err)
2879 return err;
2880 }
2881 }
2882
2883 return 0;
2884}
2885
2886static int mlxsw_sp_inetaddr_lag_event(struct net_device *lag_dev,
2887 unsigned long event)
2888{
2889 if (netif_is_bridge_port(lag_dev))
2890 return 0;
2891
2892 return __mlxsw_sp_inetaddr_lag_event(lag_dev, lag_dev, event, 1);
2893}
2894
2895static struct mlxsw_sp_fid *mlxsw_sp_bridge_fid_get(struct mlxsw_sp *mlxsw_sp,
2896 struct net_device *l3_dev)
2897{
2898 u16 fid;
2899
2900 if (is_vlan_dev(l3_dev))
2901 fid = vlan_dev_vlan_id(l3_dev);
2902 else if (mlxsw_sp->master_bridge.dev == l3_dev)
2903 fid = 1;
2904 else
2905 return mlxsw_sp_vfid_find(mlxsw_sp, l3_dev);
2906
2907 return mlxsw_sp_fid_find(mlxsw_sp, fid);
2908}
2909
2910static enum mlxsw_flood_table_type mlxsw_sp_flood_table_type_get(u16 fid)
2911{
2912 return mlxsw_sp_fid_is_vfid(fid) ? MLXSW_REG_SFGC_TABLE_TYPE_FID :
2913 MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST;
2914}
2915
2916static u16 mlxsw_sp_flood_table_index_get(u16 fid)
2917{
2918 return mlxsw_sp_fid_is_vfid(fid) ? mlxsw_sp_fid_to_vfid(fid) : fid;
2919}
2920
2921static int mlxsw_sp_router_port_flood_set(struct mlxsw_sp *mlxsw_sp, u16 fid,
2922 bool set)
2923{
2924 enum mlxsw_flood_table_type table_type;
2925 char *sftr_pl;
2926 u16 index;
2927 int err;
2928
2929 sftr_pl = kmalloc(MLXSW_REG_SFTR_LEN, GFP_KERNEL);
2930 if (!sftr_pl)
2931 return -ENOMEM;
2932
2933 table_type = mlxsw_sp_flood_table_type_get(fid);
2934 index = mlxsw_sp_flood_table_index_get(fid);
2935 mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_BC, index, table_type,
2936 1, MLXSW_PORT_ROUTER_PORT, set);
2937 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl);
2938
2939 kfree(sftr_pl);
2940 return err;
2941}
2942
2943static enum mlxsw_reg_ritr_if_type mlxsw_sp_rif_type_get(u16 fid)
2944{
2945 if (mlxsw_sp_fid_is_vfid(fid))
2946 return MLXSW_REG_RITR_FID_IF;
2947 else
2948 return MLXSW_REG_RITR_VLAN_IF;
2949}
2950
2951static int mlxsw_sp_rif_bridge_op(struct mlxsw_sp *mlxsw_sp,
2952 struct net_device *l3_dev,
2953 u16 fid, u16 rif,
2954 bool create)
2955{
2956 enum mlxsw_reg_ritr_if_type rif_type;
2957 char ritr_pl[MLXSW_REG_RITR_LEN];
2958
2959 rif_type = mlxsw_sp_rif_type_get(fid);
2960 mlxsw_reg_ritr_pack(ritr_pl, create, rif_type, rif, l3_dev->mtu,
2961 l3_dev->dev_addr);
2962 mlxsw_reg_ritr_fid_set(ritr_pl, rif_type, fid);
2963
2964 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
2965}
2966
2967static int mlxsw_sp_rif_bridge_create(struct mlxsw_sp *mlxsw_sp,
2968 struct net_device *l3_dev,
2969 struct mlxsw_sp_fid *f)
2970{
2971 struct mlxsw_sp_rif *r;
2972 u16 rif;
2973 int err;
2974
2975 rif = mlxsw_sp_avail_rif_get(mlxsw_sp);
2976 if (rif == MLXSW_SP_INVALID_RIF)
2977 return -ERANGE;
2978
2979 err = mlxsw_sp_router_port_flood_set(mlxsw_sp, f->fid, true);
2980 if (err)
2981 return err;
2982
2983 err = mlxsw_sp_rif_bridge_op(mlxsw_sp, l3_dev, f->fid, rif, true);
2984 if (err)
2985 goto err_rif_bridge_op;
2986
2987 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, f->fid, true);
2988 if (err)
2989 goto err_rif_fdb_op;
2990
2991 r = mlxsw_sp_rif_alloc(rif, l3_dev, f);
2992 if (!r) {
2993 err = -ENOMEM;
2994 goto err_rif_alloc;
2995 }
2996
2997 f->r = r;
2998 mlxsw_sp->rifs[rif] = r;
2999
3000 netdev_dbg(l3_dev, "RIF=%d created\n", rif);
3001
3002 return 0;
3003
3004err_rif_alloc:
3005 mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, f->fid, false);
3006err_rif_fdb_op:
3007 mlxsw_sp_rif_bridge_op(mlxsw_sp, l3_dev, f->fid, rif, false);
3008err_rif_bridge_op:
3009 mlxsw_sp_router_port_flood_set(mlxsw_sp, f->fid, false);
3010 return err;
3011}
3012
3013void mlxsw_sp_rif_bridge_destroy(struct mlxsw_sp *mlxsw_sp,
3014 struct mlxsw_sp_rif *r)
3015{
3016 struct net_device *l3_dev = r->dev;
3017 struct mlxsw_sp_fid *f = r->f;
3018 u16 rif = r->rif;
3019
3020 mlxsw_sp_router_rif_gone_sync(mlxsw_sp, r);
3021
3022 mlxsw_sp->rifs[rif] = NULL;
3023 f->r = NULL;
3024
3025 kfree(r);
3026
3027 mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, f->fid, false);
3028
3029 mlxsw_sp_rif_bridge_op(mlxsw_sp, l3_dev, f->fid, rif, false);
3030
3031 mlxsw_sp_router_port_flood_set(mlxsw_sp, f->fid, false);
3032
3033 netdev_dbg(l3_dev, "RIF=%d destroyed\n", rif);
3034}
3035
3036static int mlxsw_sp_inetaddr_bridge_event(struct net_device *l3_dev,
3037 struct net_device *br_dev,
3038 unsigned long event)
3039{
3040 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
3041 struct mlxsw_sp_fid *f;
3042
3043 /* FID can either be an actual FID if the L3 device is the
3044 * VLAN-aware bridge or a VLAN device on top. Otherwise, the
3045 * L3 device is a VLAN-unaware bridge and we get a vFID.
3046 */
3047 f = mlxsw_sp_bridge_fid_get(mlxsw_sp, l3_dev);
3048 if (WARN_ON(!f))
3049 return -EINVAL;
3050
3051 switch (event) {
3052 case NETDEV_UP:
3053 return mlxsw_sp_rif_bridge_create(mlxsw_sp, l3_dev, f);
3054 case NETDEV_DOWN:
3055 mlxsw_sp_rif_bridge_destroy(mlxsw_sp, f->r);
3056 break;
3057 }
3058
3059 return 0;
3060}
3061
3062static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev,
3063 unsigned long event)
3064{
3065 struct net_device *real_dev = vlan_dev_real_dev(vlan_dev);
3066 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(vlan_dev);
3067 u16 vid = vlan_dev_vlan_id(vlan_dev);
3068
3069 if (mlxsw_sp_port_dev_check(real_dev))
3070 return mlxsw_sp_inetaddr_vport_event(vlan_dev, real_dev, event,
3071 vid);
3072 else if (netif_is_lag_master(real_dev))
3073 return __mlxsw_sp_inetaddr_lag_event(vlan_dev, real_dev, event,
3074 vid);
3075 else if (netif_is_bridge_master(real_dev) &&
3076 mlxsw_sp->master_bridge.dev == real_dev)
3077 return mlxsw_sp_inetaddr_bridge_event(vlan_dev, real_dev,
3078 event);
3079
3080 return 0;
3081}
3082
3083int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
3084 unsigned long event, void *ptr)
3085{
3086 struct in_ifaddr *ifa = (struct in_ifaddr *) ptr;
3087 struct net_device *dev = ifa->ifa_dev->dev;
3088 struct mlxsw_sp *mlxsw_sp;
3089 struct mlxsw_sp_rif *r;
3090 int err = 0;
3091
3092 mlxsw_sp = mlxsw_sp_lower_get(dev);
3093 if (!mlxsw_sp)
3094 goto out;
3095
3096 r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
3097 if (!mlxsw_sp_rif_should_config(r, ifa->ifa_dev, event))
3098 goto out;
3099
3100 if (mlxsw_sp_port_dev_check(dev))
3101 err = mlxsw_sp_inetaddr_port_event(dev, event);
3102 else if (netif_is_lag_master(dev))
3103 err = mlxsw_sp_inetaddr_lag_event(dev, event);
3104 else if (netif_is_bridge_master(dev))
3105 err = mlxsw_sp_inetaddr_bridge_event(dev, dev, event);
3106 else if (is_vlan_dev(dev))
3107 err = mlxsw_sp_inetaddr_vlan_event(dev, event);
3108
3109out:
3110 return notifier_from_errno(err);
3111}
3112
3113static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif,
3114 const char *mac, int mtu)
3115{
3116 char ritr_pl[MLXSW_REG_RITR_LEN];
3117 int err;
3118
3119 mlxsw_reg_ritr_rif_pack(ritr_pl, rif);
3120 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
3121 if (err)
3122 return err;
3123
3124 mlxsw_reg_ritr_mtu_set(ritr_pl, mtu);
3125 mlxsw_reg_ritr_if_mac_memcpy_to(ritr_pl, mac);
3126 mlxsw_reg_ritr_op_set(ritr_pl, MLXSW_REG_RITR_RIF_CREATE);
3127 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
3128}
3129
3130int mlxsw_sp_netdevice_router_port_event(struct net_device *dev)
3131{
3132 struct mlxsw_sp *mlxsw_sp;
3133 struct mlxsw_sp_rif *r;
3134 int err;
3135
3136 mlxsw_sp = mlxsw_sp_lower_get(dev);
3137 if (!mlxsw_sp)
3138 return 0;
3139
3140 r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
3141 if (!r)
3142 return 0;
3143
3144 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, r->addr, r->f->fid, false);
3145 if (err)
3146 return err;
3147
3148 err = mlxsw_sp_rif_edit(mlxsw_sp, r->rif, dev->dev_addr, dev->mtu);
3149 if (err)
3150 goto err_rif_edit;
3151
3152 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, dev->dev_addr, r->f->fid, true);
3153 if (err)
3154 goto err_rif_fdb_op;
3155
3156 ether_addr_copy(r->addr, dev->dev_addr);
3157 r->mtu = dev->mtu;
3158
3159 netdev_dbg(dev, "Updated RIF=%d\n", r->rif);
3160
3161 return 0;
3162
3163err_rif_fdb_op:
3164 mlxsw_sp_rif_edit(mlxsw_sp, r->rif, r->addr, r->mtu);
3165err_rif_edit:
3166 mlxsw_sp_rif_fdb_op(mlxsw_sp, r->addr, r->f->fid, true);
3167 return err;
3168}
3169
c3852ef7
IS
3170static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb)
3171{
3172 struct mlxsw_sp *mlxsw_sp = container_of(nb, struct mlxsw_sp, fib_nb);
3173
3174 /* Flush pending FIB notifications and then flush the device's
3175 * table before requesting another dump. The FIB notification
3176 * block is unregistered, so no need to take RTNL.
3177 */
3178 mlxsw_core_flush_owq();
3179 mlxsw_sp_router_fib_flush(mlxsw_sp);
3180}
3181
4724ba56
IS
3182static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
3183{
3184 char rgcr_pl[MLXSW_REG_RGCR_LEN];
3185 u64 max_rifs;
3186 int err;
3187
3188 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_RIFS))
3189 return -EIO;
3190
3191 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
3192 mlxsw_sp->rifs = kcalloc(max_rifs, sizeof(struct mlxsw_sp_rif *),
3193 GFP_KERNEL);
3194 if (!mlxsw_sp->rifs)
3195 return -ENOMEM;
3196
3197 mlxsw_reg_rgcr_pack(rgcr_pl, true);
3198 mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, max_rifs);
3199 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
3200 if (err)
3201 goto err_rgcr_fail;
3202
3203 return 0;
3204
3205err_rgcr_fail:
3206 kfree(mlxsw_sp->rifs);
3207 return err;
3208}
3209
3210static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
3211{
3212 char rgcr_pl[MLXSW_REG_RGCR_LEN];
3213 int i;
3214
3215 mlxsw_reg_rgcr_pack(rgcr_pl, false);
3216 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
3217
3218 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
3219 WARN_ON_ONCE(mlxsw_sp->rifs[i]);
3220
3221 kfree(mlxsw_sp->rifs);
3222}
3223
b45f64d1
JP
3224int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
3225{
3226 int err;
3227
3228 INIT_LIST_HEAD(&mlxsw_sp->router.nexthop_neighs_list);
b45f64d1
JP
3229 err = __mlxsw_sp_router_init(mlxsw_sp);
3230 if (err)
3231 return err;
3232
c53b8e1b
IS
3233 err = rhashtable_init(&mlxsw_sp->router.nexthop_ht,
3234 &mlxsw_sp_nexthop_ht_params);
3235 if (err)
3236 goto err_nexthop_ht_init;
3237
e9ad5e7d
IS
3238 err = rhashtable_init(&mlxsw_sp->router.nexthop_group_ht,
3239 &mlxsw_sp_nexthop_group_ht_params);
3240 if (err)
3241 goto err_nexthop_group_ht_init;
3242
b45f64d1
JP
3243 mlxsw_sp_lpm_init(mlxsw_sp);
3244 err = mlxsw_sp_vrs_init(mlxsw_sp);
3245 if (err)
3246 goto err_vrs_init;
3247
8c9583a8 3248 err = mlxsw_sp_neigh_init(mlxsw_sp);
b45f64d1
JP
3249 if (err)
3250 goto err_neigh_init;
3251
3252 mlxsw_sp->fib_nb.notifier_call = mlxsw_sp_router_fib_event;
c3852ef7
IS
3253 err = register_fib_notifier(&mlxsw_sp->fib_nb,
3254 mlxsw_sp_router_fib_dump_flush);
3255 if (err)
3256 goto err_register_fib_notifier;
3257
b45f64d1
JP
3258 return 0;
3259
c3852ef7
IS
3260err_register_fib_notifier:
3261 mlxsw_sp_neigh_fini(mlxsw_sp);
b45f64d1
JP
3262err_neigh_init:
3263 mlxsw_sp_vrs_fini(mlxsw_sp);
3264err_vrs_init:
e9ad5e7d
IS
3265 rhashtable_destroy(&mlxsw_sp->router.nexthop_group_ht);
3266err_nexthop_group_ht_init:
c53b8e1b
IS
3267 rhashtable_destroy(&mlxsw_sp->router.nexthop_ht);
3268err_nexthop_ht_init:
b45f64d1
JP
3269 __mlxsw_sp_router_fini(mlxsw_sp);
3270 return err;
3271}
3272
3273void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
3274{
3275 unregister_fib_notifier(&mlxsw_sp->fib_nb);
3276 mlxsw_sp_neigh_fini(mlxsw_sp);
3277 mlxsw_sp_vrs_fini(mlxsw_sp);
e9ad5e7d 3278 rhashtable_destroy(&mlxsw_sp->router.nexthop_group_ht);
c53b8e1b 3279 rhashtable_destroy(&mlxsw_sp->router.nexthop_ht);
b45f64d1
JP
3280 __mlxsw_sp_router_fini(mlxsw_sp);
3281}