]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
mlxsw: spectrum_router: Allow more route types to be programmed
[mirror_ubuntu-artful-kernel.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
53342023
JP
53#define mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) \
54 for_each_set_bit(prefix, (prefix_usage)->b, MLXSW_SP_PREFIX_COUNT)
55
6b75c480
JP
56static bool
57mlxsw_sp_prefix_usage_subset(struct mlxsw_sp_prefix_usage *prefix_usage1,
58 struct mlxsw_sp_prefix_usage *prefix_usage2)
59{
60 unsigned char prefix;
61
62 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage1) {
63 if (!test_bit(prefix, prefix_usage2->b))
64 return false;
65 }
66 return true;
67}
68
53342023
JP
69static bool
70mlxsw_sp_prefix_usage_eq(struct mlxsw_sp_prefix_usage *prefix_usage1,
71 struct mlxsw_sp_prefix_usage *prefix_usage2)
72{
73 return !memcmp(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
74}
75
6b75c480
JP
76static bool
77mlxsw_sp_prefix_usage_none(struct mlxsw_sp_prefix_usage *prefix_usage)
78{
79 struct mlxsw_sp_prefix_usage prefix_usage_none = {{ 0 } };
80
81 return mlxsw_sp_prefix_usage_eq(prefix_usage, &prefix_usage_none);
82}
83
84static void
85mlxsw_sp_prefix_usage_cpy(struct mlxsw_sp_prefix_usage *prefix_usage1,
86 struct mlxsw_sp_prefix_usage *prefix_usage2)
87{
88 memcpy(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
89}
90
91static void
92mlxsw_sp_prefix_usage_zero(struct mlxsw_sp_prefix_usage *prefix_usage)
93{
94 memset(prefix_usage, 0, sizeof(*prefix_usage));
95}
96
5e9c16cc
JP
97static void
98mlxsw_sp_prefix_usage_set(struct mlxsw_sp_prefix_usage *prefix_usage,
99 unsigned char prefix_len)
100{
101 set_bit(prefix_len, prefix_usage->b);
102}
103
104static void
105mlxsw_sp_prefix_usage_clear(struct mlxsw_sp_prefix_usage *prefix_usage,
106 unsigned char prefix_len)
107{
108 clear_bit(prefix_len, prefix_usage->b);
109}
110
111struct mlxsw_sp_fib_key {
112 unsigned char addr[sizeof(struct in6_addr)];
113 unsigned char prefix_len;
114};
115
61c503f9
JP
116enum mlxsw_sp_fib_entry_type {
117 MLXSW_SP_FIB_ENTRY_TYPE_REMOTE,
118 MLXSW_SP_FIB_ENTRY_TYPE_LOCAL,
119 MLXSW_SP_FIB_ENTRY_TYPE_TRAP,
120};
121
a7ff87ac
JP
122struct mlxsw_sp_nexthop_group;
123
9aecce1c
IS
124struct mlxsw_sp_fib_node {
125 struct list_head entry_list;
b45f64d1 126 struct list_head list;
9aecce1c
IS
127 struct rhash_head ht_node;
128 struct mlxsw_sp_vr *vr;
5e9c16cc 129 struct mlxsw_sp_fib_key key;
9aecce1c
IS
130};
131
132struct mlxsw_sp_fib_entry_params {
133 u32 tb_id;
134 u32 prio;
135 u8 tos;
136 u8 type;
137};
138
139struct mlxsw_sp_fib_entry {
140 struct list_head list;
141 struct mlxsw_sp_fib_node *fib_node;
61c503f9 142 enum mlxsw_sp_fib_entry_type type;
a7ff87ac
JP
143 struct list_head nexthop_group_node;
144 struct mlxsw_sp_nexthop_group *nh_group;
9aecce1c 145 struct mlxsw_sp_fib_entry_params params;
013b20f9 146 bool offloaded;
5e9c16cc
JP
147};
148
149struct mlxsw_sp_fib {
150 struct rhashtable ht;
9aecce1c 151 struct list_head node_list;
5e9c16cc
JP
152 unsigned long prefix_ref_count[MLXSW_SP_PREFIX_COUNT];
153 struct mlxsw_sp_prefix_usage prefix_usage;
154};
155
9aecce1c 156static const struct rhashtable_params mlxsw_sp_fib_ht_params;
5e9c16cc
JP
157
158static struct mlxsw_sp_fib *mlxsw_sp_fib_create(void)
159{
160 struct mlxsw_sp_fib *fib;
161 int err;
162
163 fib = kzalloc(sizeof(*fib), GFP_KERNEL);
164 if (!fib)
165 return ERR_PTR(-ENOMEM);
166 err = rhashtable_init(&fib->ht, &mlxsw_sp_fib_ht_params);
167 if (err)
168 goto err_rhashtable_init;
9aecce1c 169 INIT_LIST_HEAD(&fib->node_list);
5e9c16cc
JP
170 return fib;
171
172err_rhashtable_init:
173 kfree(fib);
174 return ERR_PTR(err);
175}
176
177static void mlxsw_sp_fib_destroy(struct mlxsw_sp_fib *fib)
178{
9aecce1c 179 WARN_ON(!list_empty(&fib->node_list));
5e9c16cc
JP
180 rhashtable_destroy(&fib->ht);
181 kfree(fib);
182}
183
53342023
JP
184static struct mlxsw_sp_lpm_tree *
185mlxsw_sp_lpm_tree_find_unused(struct mlxsw_sp *mlxsw_sp, bool one_reserved)
186{
187 static struct mlxsw_sp_lpm_tree *lpm_tree;
188 int i;
189
190 for (i = 0; i < MLXSW_SP_LPM_TREE_COUNT; i++) {
191 lpm_tree = &mlxsw_sp->router.lpm_trees[i];
192 if (lpm_tree->ref_count == 0) {
193 if (one_reserved)
194 one_reserved = false;
195 else
196 return lpm_tree;
197 }
198 }
199 return NULL;
200}
201
202static int mlxsw_sp_lpm_tree_alloc(struct mlxsw_sp *mlxsw_sp,
203 struct mlxsw_sp_lpm_tree *lpm_tree)
204{
205 char ralta_pl[MLXSW_REG_RALTA_LEN];
206
1a9234e6
IS
207 mlxsw_reg_ralta_pack(ralta_pl, true,
208 (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
209 lpm_tree->id);
53342023
JP
210 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
211}
212
213static int mlxsw_sp_lpm_tree_free(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, false,
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
225mlxsw_sp_lpm_tree_left_struct_set(struct mlxsw_sp *mlxsw_sp,
226 struct mlxsw_sp_prefix_usage *prefix_usage,
227 struct mlxsw_sp_lpm_tree *lpm_tree)
228{
229 char ralst_pl[MLXSW_REG_RALST_LEN];
230 u8 root_bin = 0;
231 u8 prefix;
232 u8 last_prefix = MLXSW_REG_RALST_BIN_NO_CHILD;
233
234 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage)
235 root_bin = prefix;
236
237 mlxsw_reg_ralst_pack(ralst_pl, root_bin, lpm_tree->id);
238 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) {
239 if (prefix == 0)
240 continue;
241 mlxsw_reg_ralst_bin_pack(ralst_pl, prefix, last_prefix,
242 MLXSW_REG_RALST_BIN_NO_CHILD);
243 last_prefix = prefix;
244 }
245 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
246}
247
248static struct mlxsw_sp_lpm_tree *
249mlxsw_sp_lpm_tree_create(struct mlxsw_sp *mlxsw_sp,
250 struct mlxsw_sp_prefix_usage *prefix_usage,
251 enum mlxsw_sp_l3proto proto, bool one_reserved)
252{
253 struct mlxsw_sp_lpm_tree *lpm_tree;
254 int err;
255
256 lpm_tree = mlxsw_sp_lpm_tree_find_unused(mlxsw_sp, one_reserved);
257 if (!lpm_tree)
258 return ERR_PTR(-EBUSY);
259 lpm_tree->proto = proto;
260 err = mlxsw_sp_lpm_tree_alloc(mlxsw_sp, lpm_tree);
261 if (err)
262 return ERR_PTR(err);
263
264 err = mlxsw_sp_lpm_tree_left_struct_set(mlxsw_sp, prefix_usage,
265 lpm_tree);
266 if (err)
267 goto err_left_struct_set;
2083d367
JP
268 memcpy(&lpm_tree->prefix_usage, prefix_usage,
269 sizeof(lpm_tree->prefix_usage));
53342023
JP
270 return lpm_tree;
271
272err_left_struct_set:
273 mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
274 return ERR_PTR(err);
275}
276
277static int mlxsw_sp_lpm_tree_destroy(struct mlxsw_sp *mlxsw_sp,
278 struct mlxsw_sp_lpm_tree *lpm_tree)
279{
280 return mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
281}
282
283static struct mlxsw_sp_lpm_tree *
284mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp,
285 struct mlxsw_sp_prefix_usage *prefix_usage,
286 enum mlxsw_sp_l3proto proto, bool one_reserved)
287{
288 struct mlxsw_sp_lpm_tree *lpm_tree;
289 int i;
290
291 for (i = 0; i < MLXSW_SP_LPM_TREE_COUNT; i++) {
292 lpm_tree = &mlxsw_sp->router.lpm_trees[i];
8b99becd
JP
293 if (lpm_tree->ref_count != 0 &&
294 lpm_tree->proto == proto &&
53342023
JP
295 mlxsw_sp_prefix_usage_eq(&lpm_tree->prefix_usage,
296 prefix_usage))
297 goto inc_ref_count;
298 }
299 lpm_tree = mlxsw_sp_lpm_tree_create(mlxsw_sp, prefix_usage,
300 proto, one_reserved);
301 if (IS_ERR(lpm_tree))
302 return lpm_tree;
303
304inc_ref_count:
305 lpm_tree->ref_count++;
306 return lpm_tree;
307}
308
309static int mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp,
310 struct mlxsw_sp_lpm_tree *lpm_tree)
311{
312 if (--lpm_tree->ref_count == 0)
313 return mlxsw_sp_lpm_tree_destroy(mlxsw_sp, lpm_tree);
314 return 0;
315}
316
317static void mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp)
318{
319 struct mlxsw_sp_lpm_tree *lpm_tree;
320 int i;
321
322 for (i = 0; i < MLXSW_SP_LPM_TREE_COUNT; i++) {
323 lpm_tree = &mlxsw_sp->router.lpm_trees[i];
324 lpm_tree->id = i + MLXSW_SP_LPM_TREE_MIN;
325 }
326}
327
6b75c480
JP
328static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp)
329{
330 struct mlxsw_sp_vr *vr;
331 int i;
332
c1a38311 333 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
6b75c480
JP
334 vr = &mlxsw_sp->router.vrs[i];
335 if (!vr->used)
336 return vr;
337 }
338 return NULL;
339}
340
341static int mlxsw_sp_vr_lpm_tree_bind(struct mlxsw_sp *mlxsw_sp,
342 struct mlxsw_sp_vr *vr)
343{
344 char raltb_pl[MLXSW_REG_RALTB_LEN];
345
1a9234e6
IS
346 mlxsw_reg_raltb_pack(raltb_pl, vr->id,
347 (enum mlxsw_reg_ralxx_protocol) vr->proto,
348 vr->lpm_tree->id);
6b75c480
JP
349 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
350}
351
352static int mlxsw_sp_vr_lpm_tree_unbind(struct mlxsw_sp *mlxsw_sp,
353 struct mlxsw_sp_vr *vr)
354{
355 char raltb_pl[MLXSW_REG_RALTB_LEN];
356
357 /* Bind to tree 0 which is default */
1a9234e6
IS
358 mlxsw_reg_raltb_pack(raltb_pl, vr->id,
359 (enum mlxsw_reg_ralxx_protocol) vr->proto, 0);
6b75c480
JP
360 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
361}
362
363static u32 mlxsw_sp_fix_tb_id(u32 tb_id)
364{
365 /* For our purpose, squash main and local table into one */
366 if (tb_id == RT_TABLE_LOCAL)
367 tb_id = RT_TABLE_MAIN;
368 return tb_id;
369}
370
371static struct mlxsw_sp_vr *mlxsw_sp_vr_find(struct mlxsw_sp *mlxsw_sp,
372 u32 tb_id,
373 enum mlxsw_sp_l3proto proto)
374{
375 struct mlxsw_sp_vr *vr;
376 int i;
377
378 tb_id = mlxsw_sp_fix_tb_id(tb_id);
9497c042 379
c1a38311 380 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
6b75c480
JP
381 vr = &mlxsw_sp->router.vrs[i];
382 if (vr->used && vr->proto == proto && vr->tb_id == tb_id)
383 return vr;
384 }
385 return NULL;
386}
387
388static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp,
389 unsigned char prefix_len,
390 u32 tb_id,
391 enum mlxsw_sp_l3proto proto)
392{
393 struct mlxsw_sp_prefix_usage req_prefix_usage;
394 struct mlxsw_sp_lpm_tree *lpm_tree;
395 struct mlxsw_sp_vr *vr;
396 int err;
397
398 vr = mlxsw_sp_vr_find_unused(mlxsw_sp);
399 if (!vr)
400 return ERR_PTR(-EBUSY);
401 vr->fib = mlxsw_sp_fib_create();
402 if (IS_ERR(vr->fib))
403 return ERR_CAST(vr->fib);
404
405 vr->proto = proto;
406 vr->tb_id = tb_id;
407 mlxsw_sp_prefix_usage_zero(&req_prefix_usage);
408 mlxsw_sp_prefix_usage_set(&req_prefix_usage, prefix_len);
409 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
410 proto, true);
411 if (IS_ERR(lpm_tree)) {
412 err = PTR_ERR(lpm_tree);
413 goto err_tree_get;
414 }
415 vr->lpm_tree = lpm_tree;
416 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, vr);
417 if (err)
418 goto err_tree_bind;
419
420 vr->used = true;
421 return vr;
422
423err_tree_bind:
424 mlxsw_sp_lpm_tree_put(mlxsw_sp, vr->lpm_tree);
425err_tree_get:
426 mlxsw_sp_fib_destroy(vr->fib);
427
428 return ERR_PTR(err);
429}
430
431static void mlxsw_sp_vr_destroy(struct mlxsw_sp *mlxsw_sp,
432 struct mlxsw_sp_vr *vr)
433{
434 mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, vr);
435 mlxsw_sp_lpm_tree_put(mlxsw_sp, vr->lpm_tree);
436 mlxsw_sp_fib_destroy(vr->fib);
437 vr->used = false;
438}
439
440static int
441mlxsw_sp_vr_lpm_tree_check(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr,
442 struct mlxsw_sp_prefix_usage *req_prefix_usage)
443{
f7df4923
IS
444 struct mlxsw_sp_lpm_tree *lpm_tree = vr->lpm_tree;
445 struct mlxsw_sp_lpm_tree *new_tree;
446 int err;
6b75c480 447
f7df4923 448 if (mlxsw_sp_prefix_usage_eq(req_prefix_usage, &lpm_tree->prefix_usage))
6b75c480
JP
449 return 0;
450
f7df4923 451 new_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, req_prefix_usage,
6b75c480 452 vr->proto, false);
f7df4923 453 if (IS_ERR(new_tree)) {
6b75c480
JP
454 /* We failed to get a tree according to the required
455 * prefix usage. However, the current tree might be still good
456 * for us if our requirement is subset of the prefixes used
457 * in the tree.
458 */
459 if (mlxsw_sp_prefix_usage_subset(req_prefix_usage,
f7df4923 460 &lpm_tree->prefix_usage))
6b75c480 461 return 0;
f7df4923 462 return PTR_ERR(new_tree);
6b75c480
JP
463 }
464
f7df4923
IS
465 /* Prevent packet loss by overwriting existing binding */
466 vr->lpm_tree = new_tree;
467 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, vr);
468 if (err)
469 goto err_tree_bind;
470 mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree);
471
472 return 0;
473
474err_tree_bind:
6b75c480 475 vr->lpm_tree = lpm_tree;
f7df4923
IS
476 mlxsw_sp_lpm_tree_put(mlxsw_sp, new_tree);
477 return err;
6b75c480
JP
478}
479
480static struct mlxsw_sp_vr *mlxsw_sp_vr_get(struct mlxsw_sp *mlxsw_sp,
481 unsigned char prefix_len,
482 u32 tb_id,
483 enum mlxsw_sp_l3proto proto)
484{
485 struct mlxsw_sp_vr *vr;
486 int err;
487
488 tb_id = mlxsw_sp_fix_tb_id(tb_id);
489 vr = mlxsw_sp_vr_find(mlxsw_sp, tb_id, proto);
490 if (!vr) {
491 vr = mlxsw_sp_vr_create(mlxsw_sp, prefix_len, tb_id, proto);
492 if (IS_ERR(vr))
493 return vr;
494 } else {
495 struct mlxsw_sp_prefix_usage req_prefix_usage;
496
497 mlxsw_sp_prefix_usage_cpy(&req_prefix_usage,
498 &vr->fib->prefix_usage);
499 mlxsw_sp_prefix_usage_set(&req_prefix_usage, prefix_len);
500 /* Need to replace LPM tree in case new prefix is required. */
501 err = mlxsw_sp_vr_lpm_tree_check(mlxsw_sp, vr,
502 &req_prefix_usage);
503 if (err)
504 return ERR_PTR(err);
505 }
506 return vr;
507}
508
509static void mlxsw_sp_vr_put(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr)
510{
511 /* Destroy virtual router entity in case the associated FIB is empty
512 * and allow it to be used for other tables in future. Otherwise,
513 * check if some prefix usage did not disappear and change tree if
514 * that is the case. Note that in case new, smaller tree cannot be
515 * allocated, the original one will be kept being used.
516 */
517 if (mlxsw_sp_prefix_usage_none(&vr->fib->prefix_usage))
518 mlxsw_sp_vr_destroy(mlxsw_sp, vr);
519 else
520 mlxsw_sp_vr_lpm_tree_check(mlxsw_sp, vr,
521 &vr->fib->prefix_usage);
522}
523
9497c042 524static int mlxsw_sp_vrs_init(struct mlxsw_sp *mlxsw_sp)
6b75c480
JP
525{
526 struct mlxsw_sp_vr *vr;
c1a38311 527 u64 max_vrs;
6b75c480
JP
528 int i;
529
c1a38311 530 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_VRS))
9497c042
NF
531 return -EIO;
532
c1a38311
JP
533 max_vrs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS);
534 mlxsw_sp->router.vrs = kcalloc(max_vrs, sizeof(struct mlxsw_sp_vr),
9497c042
NF
535 GFP_KERNEL);
536 if (!mlxsw_sp->router.vrs)
537 return -ENOMEM;
538
c1a38311 539 for (i = 0; i < max_vrs; i++) {
6b75c480
JP
540 vr = &mlxsw_sp->router.vrs[i];
541 vr->id = i;
542 }
9497c042
NF
543
544 return 0;
545}
546
ac571de9
IS
547static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp);
548
9497c042
NF
549static void mlxsw_sp_vrs_fini(struct mlxsw_sp *mlxsw_sp)
550{
3057224e
IS
551 /* At this stage we're guaranteed not to have new incoming
552 * FIB notifications and the work queue is free from FIBs
553 * sitting on top of mlxsw netdevs. However, we can still
554 * have other FIBs queued. Flush the queue before flushing
555 * the device's tables. No need for locks, as we're the only
556 * writer.
557 */
558 mlxsw_core_flush_owq();
ac571de9 559 mlxsw_sp_router_fib_flush(mlxsw_sp);
9497c042 560 kfree(mlxsw_sp->router.vrs);
6b75c480
JP
561}
562
6cf3c971 563struct mlxsw_sp_neigh_key {
33b1341c 564 struct neighbour *n;
6cf3c971
JP
565};
566
567struct mlxsw_sp_neigh_entry {
9665b745 568 struct list_head rif_list_node;
6cf3c971
JP
569 struct rhash_head ht_node;
570 struct mlxsw_sp_neigh_key key;
571 u16 rif;
5c8802f1 572 bool connected;
a6bf9e93 573 unsigned char ha[ETH_ALEN];
a7ff87ac
JP
574 struct list_head nexthop_list; /* list of nexthops using
575 * this neigh entry
576 */
b2157149 577 struct list_head nexthop_neighs_list_node;
6cf3c971
JP
578};
579
580static const struct rhashtable_params mlxsw_sp_neigh_ht_params = {
581 .key_offset = offsetof(struct mlxsw_sp_neigh_entry, key),
582 .head_offset = offsetof(struct mlxsw_sp_neigh_entry, ht_node),
583 .key_len = sizeof(struct mlxsw_sp_neigh_key),
584};
585
6cf3c971 586static struct mlxsw_sp_neigh_entry *
5c8802f1
IS
587mlxsw_sp_neigh_entry_alloc(struct mlxsw_sp *mlxsw_sp, struct neighbour *n,
588 u16 rif)
6cf3c971
JP
589{
590 struct mlxsw_sp_neigh_entry *neigh_entry;
591
5c8802f1 592 neigh_entry = kzalloc(sizeof(*neigh_entry), GFP_KERNEL);
6cf3c971
JP
593 if (!neigh_entry)
594 return NULL;
5c8802f1 595
33b1341c 596 neigh_entry->key.n = n;
6cf3c971 597 neigh_entry->rif = rif;
a7ff87ac 598 INIT_LIST_HEAD(&neigh_entry->nexthop_list);
5c8802f1 599
6cf3c971
JP
600 return neigh_entry;
601}
602
5c8802f1 603static void mlxsw_sp_neigh_entry_free(struct mlxsw_sp_neigh_entry *neigh_entry)
6cf3c971
JP
604{
605 kfree(neigh_entry);
606}
607
5c8802f1
IS
608static int
609mlxsw_sp_neigh_entry_insert(struct mlxsw_sp *mlxsw_sp,
610 struct mlxsw_sp_neigh_entry *neigh_entry)
6cf3c971 611{
5c8802f1
IS
612 return rhashtable_insert_fast(&mlxsw_sp->router.neigh_ht,
613 &neigh_entry->ht_node,
614 mlxsw_sp_neigh_ht_params);
615}
6cf3c971 616
5c8802f1
IS
617static void
618mlxsw_sp_neigh_entry_remove(struct mlxsw_sp *mlxsw_sp,
619 struct mlxsw_sp_neigh_entry *neigh_entry)
620{
621 rhashtable_remove_fast(&mlxsw_sp->router.neigh_ht,
622 &neigh_entry->ht_node,
623 mlxsw_sp_neigh_ht_params);
6cf3c971
JP
624}
625
5c8802f1
IS
626static struct mlxsw_sp_neigh_entry *
627mlxsw_sp_neigh_entry_create(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
6cf3c971 628{
6cf3c971
JP
629 struct mlxsw_sp_neigh_entry *neigh_entry;
630 struct mlxsw_sp_rif *r;
6cf3c971
JP
631 int err;
632
51af96b5 633 r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, n->dev);
5c8802f1
IS
634 if (!r)
635 return ERR_PTR(-EINVAL);
6cf3c971 636
5c8802f1 637 neigh_entry = mlxsw_sp_neigh_entry_alloc(mlxsw_sp, n, r->rif);
6cf3c971 638 if (!neigh_entry)
5c8802f1
IS
639 return ERR_PTR(-ENOMEM);
640
6cf3c971
JP
641 err = mlxsw_sp_neigh_entry_insert(mlxsw_sp, neigh_entry);
642 if (err)
643 goto err_neigh_entry_insert;
5c8802f1 644
9665b745
IS
645 list_add(&neigh_entry->rif_list_node, &r->neigh_list);
646
5c8802f1 647 return neigh_entry;
6cf3c971
JP
648
649err_neigh_entry_insert:
5c8802f1
IS
650 mlxsw_sp_neigh_entry_free(neigh_entry);
651 return ERR_PTR(err);
6cf3c971
JP
652}
653
5c8802f1
IS
654static void
655mlxsw_sp_neigh_entry_destroy(struct mlxsw_sp *mlxsw_sp,
656 struct mlxsw_sp_neigh_entry *neigh_entry)
6cf3c971 657{
9665b745 658 list_del(&neigh_entry->rif_list_node);
5c8802f1
IS
659 mlxsw_sp_neigh_entry_remove(mlxsw_sp, neigh_entry);
660 mlxsw_sp_neigh_entry_free(neigh_entry);
661}
6cf3c971 662
5c8802f1
IS
663static struct mlxsw_sp_neigh_entry *
664mlxsw_sp_neigh_entry_lookup(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
665{
666 struct mlxsw_sp_neigh_key key;
6cf3c971 667
5c8802f1
IS
668 key.n = n;
669 return rhashtable_lookup_fast(&mlxsw_sp->router.neigh_ht,
670 &key, mlxsw_sp_neigh_ht_params);
6cf3c971
JP
671}
672
c723c735
YG
673static void
674mlxsw_sp_router_neighs_update_interval_init(struct mlxsw_sp *mlxsw_sp)
675{
676 unsigned long interval = NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME);
677
678 mlxsw_sp->router.neighs_update.interval = jiffies_to_msecs(interval);
679}
680
681static void mlxsw_sp_router_neigh_ent_ipv4_process(struct mlxsw_sp *mlxsw_sp,
682 char *rauhtd_pl,
683 int ent_index)
684{
685 struct net_device *dev;
686 struct neighbour *n;
687 __be32 dipn;
688 u32 dip;
689 u16 rif;
690
691 mlxsw_reg_rauhtd_ent_ipv4_unpack(rauhtd_pl, ent_index, &rif, &dip);
692
693 if (!mlxsw_sp->rifs[rif]) {
694 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
695 return;
696 }
697
698 dipn = htonl(dip);
699 dev = mlxsw_sp->rifs[rif]->dev;
700 n = neigh_lookup(&arp_tbl, &dipn, dev);
701 if (!n) {
702 netdev_err(dev, "Failed to find matching neighbour for IP=%pI4h\n",
703 &dip);
704 return;
705 }
706
707 netdev_dbg(dev, "Updating neighbour with IP=%pI4h\n", &dip);
708 neigh_event_send(n, NULL);
709 neigh_release(n);
710}
711
712static void mlxsw_sp_router_neigh_rec_ipv4_process(struct mlxsw_sp *mlxsw_sp,
713 char *rauhtd_pl,
714 int rec_index)
715{
716 u8 num_entries;
717 int i;
718
719 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
720 rec_index);
721 /* Hardware starts counting at 0, so add 1. */
722 num_entries++;
723
724 /* Each record consists of several neighbour entries. */
725 for (i = 0; i < num_entries; i++) {
726 int ent_index;
727
728 ent_index = rec_index * MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC + i;
729 mlxsw_sp_router_neigh_ent_ipv4_process(mlxsw_sp, rauhtd_pl,
730 ent_index);
731 }
732
733}
734
735static void mlxsw_sp_router_neigh_rec_process(struct mlxsw_sp *mlxsw_sp,
736 char *rauhtd_pl, int rec_index)
737{
738 switch (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, rec_index)) {
739 case MLXSW_REG_RAUHTD_TYPE_IPV4:
740 mlxsw_sp_router_neigh_rec_ipv4_process(mlxsw_sp, rauhtd_pl,
741 rec_index);
742 break;
743 case MLXSW_REG_RAUHTD_TYPE_IPV6:
744 WARN_ON_ONCE(1);
745 break;
746 }
747}
748
42cdb338
AS
749static bool mlxsw_sp_router_rauhtd_is_full(char *rauhtd_pl)
750{
751 u8 num_rec, last_rec_index, num_entries;
752
753 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
754 last_rec_index = num_rec - 1;
755
756 if (num_rec < MLXSW_REG_RAUHTD_REC_MAX_NUM)
757 return false;
758 if (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, last_rec_index) ==
759 MLXSW_REG_RAUHTD_TYPE_IPV6)
760 return true;
761
762 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
763 last_rec_index);
764 if (++num_entries == MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC)
765 return true;
766 return false;
767}
768
b2157149 769static int mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp)
c723c735 770{
c723c735
YG
771 char *rauhtd_pl;
772 u8 num_rec;
773 int i, err;
774
775 rauhtd_pl = kmalloc(MLXSW_REG_RAUHTD_LEN, GFP_KERNEL);
776 if (!rauhtd_pl)
b2157149 777 return -ENOMEM;
c723c735
YG
778
779 /* Make sure the neighbour's netdev isn't removed in the
780 * process.
781 */
782 rtnl_lock();
783 do {
784 mlxsw_reg_rauhtd_pack(rauhtd_pl, MLXSW_REG_RAUHTD_TYPE_IPV4);
785 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(rauhtd),
786 rauhtd_pl);
787 if (err) {
788 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to dump neighbour talbe\n");
789 break;
790 }
791 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
792 for (i = 0; i < num_rec; i++)
793 mlxsw_sp_router_neigh_rec_process(mlxsw_sp, rauhtd_pl,
794 i);
42cdb338 795 } while (mlxsw_sp_router_rauhtd_is_full(rauhtd_pl));
c723c735
YG
796 rtnl_unlock();
797
798 kfree(rauhtd_pl);
b2157149
YG
799 return err;
800}
801
802static void mlxsw_sp_router_neighs_update_nh(struct mlxsw_sp *mlxsw_sp)
803{
804 struct mlxsw_sp_neigh_entry *neigh_entry;
805
806 /* Take RTNL mutex here to prevent lists from changes */
807 rtnl_lock();
808 list_for_each_entry(neigh_entry, &mlxsw_sp->router.nexthop_neighs_list,
8a0b7275 809 nexthop_neighs_list_node)
b2157149
YG
810 /* If this neigh have nexthops, make the kernel think this neigh
811 * is active regardless of the traffic.
812 */
8a0b7275 813 neigh_event_send(neigh_entry->key.n, NULL);
b2157149
YG
814 rtnl_unlock();
815}
816
817static void
818mlxsw_sp_router_neighs_update_work_schedule(struct mlxsw_sp *mlxsw_sp)
819{
820 unsigned long interval = mlxsw_sp->router.neighs_update.interval;
821
822 mlxsw_core_schedule_dw(&mlxsw_sp->router.neighs_update.dw,
823 msecs_to_jiffies(interval));
824}
825
826static void mlxsw_sp_router_neighs_update_work(struct work_struct *work)
827{
828 struct mlxsw_sp *mlxsw_sp = container_of(work, struct mlxsw_sp,
829 router.neighs_update.dw.work);
830 int err;
831
832 err = mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp);
833 if (err)
834 dev_err(mlxsw_sp->bus_info->dev, "Could not update kernel for neigh activity");
835
836 mlxsw_sp_router_neighs_update_nh(mlxsw_sp);
837
c723c735
YG
838 mlxsw_sp_router_neighs_update_work_schedule(mlxsw_sp);
839}
840
0b2361d9
YG
841static void mlxsw_sp_router_probe_unresolved_nexthops(struct work_struct *work)
842{
843 struct mlxsw_sp_neigh_entry *neigh_entry;
844 struct mlxsw_sp *mlxsw_sp = container_of(work, struct mlxsw_sp,
845 router.nexthop_probe_dw.work);
846
847 /* Iterate over nexthop neighbours, find those who are unresolved and
848 * send arp on them. This solves the chicken-egg problem when
849 * the nexthop wouldn't get offloaded until the neighbor is resolved
850 * but it wouldn't get resolved ever in case traffic is flowing in HW
851 * using different nexthop.
852 *
853 * Take RTNL mutex here to prevent lists from changes.
854 */
855 rtnl_lock();
856 list_for_each_entry(neigh_entry, &mlxsw_sp->router.nexthop_neighs_list,
8a0b7275 857 nexthop_neighs_list_node)
01b1aa35 858 if (!neigh_entry->connected)
33b1341c 859 neigh_event_send(neigh_entry->key.n, NULL);
0b2361d9
YG
860 rtnl_unlock();
861
862 mlxsw_core_schedule_dw(&mlxsw_sp->router.nexthop_probe_dw,
863 MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL);
864}
865
a7ff87ac
JP
866static void
867mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
868 struct mlxsw_sp_neigh_entry *neigh_entry,
869 bool removing);
870
5c8802f1
IS
871static enum mlxsw_reg_rauht_op mlxsw_sp_rauht_op(bool adding)
872{
873 return adding ? MLXSW_REG_RAUHT_OP_WRITE_ADD :
874 MLXSW_REG_RAUHT_OP_WRITE_DELETE;
875}
876
877static void
878mlxsw_sp_router_neigh_entry_op4(struct mlxsw_sp *mlxsw_sp,
879 struct mlxsw_sp_neigh_entry *neigh_entry,
880 enum mlxsw_reg_rauht_op op)
a6bf9e93 881{
33b1341c 882 struct neighbour *n = neigh_entry->key.n;
5c8802f1 883 u32 dip = ntohl(*((__be32 *) n->primary_key));
a6bf9e93 884 char rauht_pl[MLXSW_REG_RAUHT_LEN];
5c8802f1
IS
885
886 mlxsw_reg_rauht_pack4(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
887 dip);
888 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
889}
890
891static void
892mlxsw_sp_neigh_entry_update(struct mlxsw_sp *mlxsw_sp,
893 struct mlxsw_sp_neigh_entry *neigh_entry,
894 bool adding)
895{
896 if (!adding && !neigh_entry->connected)
897 return;
898 neigh_entry->connected = adding;
899 if (neigh_entry->key.n->tbl == &arp_tbl)
900 mlxsw_sp_router_neigh_entry_op4(mlxsw_sp, neigh_entry,
901 mlxsw_sp_rauht_op(adding));
902 else
903 WARN_ON_ONCE(1);
904}
905
906struct mlxsw_sp_neigh_event_work {
907 struct work_struct work;
908 struct mlxsw_sp *mlxsw_sp;
909 struct neighbour *n;
910};
911
912static void mlxsw_sp_router_neigh_event_work(struct work_struct *work)
913{
914 struct mlxsw_sp_neigh_event_work *neigh_work =
915 container_of(work, struct mlxsw_sp_neigh_event_work, work);
916 struct mlxsw_sp *mlxsw_sp = neigh_work->mlxsw_sp;
917 struct mlxsw_sp_neigh_entry *neigh_entry;
918 struct neighbour *n = neigh_work->n;
919 unsigned char ha[ETH_ALEN];
a6bf9e93 920 bool entry_connected;
93a87e5e 921 u8 nud_state, dead;
a6bf9e93 922
5c8802f1
IS
923 /* If these parameters are changed after we release the lock,
924 * then we are guaranteed to receive another event letting us
925 * know about it.
926 */
a6bf9e93 927 read_lock_bh(&n->lock);
5c8802f1 928 memcpy(ha, n->ha, ETH_ALEN);
a6bf9e93 929 nud_state = n->nud_state;
93a87e5e 930 dead = n->dead;
a6bf9e93
YG
931 read_unlock_bh(&n->lock);
932
5c8802f1 933 rtnl_lock();
93a87e5e 934 entry_connected = nud_state & NUD_VALID && !dead;
5c8802f1
IS
935 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
936 if (!entry_connected && !neigh_entry)
937 goto out;
938 if (!neigh_entry) {
939 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
940 if (IS_ERR(neigh_entry))
941 goto out;
a6bf9e93
YG
942 }
943
5c8802f1
IS
944 memcpy(neigh_entry->ha, ha, ETH_ALEN);
945 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, entry_connected);
946 mlxsw_sp_nexthop_neigh_update(mlxsw_sp, neigh_entry, !entry_connected);
947
948 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
949 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
950
951out:
952 rtnl_unlock();
a6bf9e93 953 neigh_release(n);
5c8802f1 954 kfree(neigh_work);
a6bf9e93
YG
955}
956
e7322638
JP
957int mlxsw_sp_router_netevent_event(struct notifier_block *unused,
958 unsigned long event, void *ptr)
c723c735 959{
5c8802f1 960 struct mlxsw_sp_neigh_event_work *neigh_work;
c723c735
YG
961 struct mlxsw_sp_port *mlxsw_sp_port;
962 struct mlxsw_sp *mlxsw_sp;
963 unsigned long interval;
964 struct neigh_parms *p;
a6bf9e93 965 struct neighbour *n;
c723c735
YG
966
967 switch (event) {
968 case NETEVENT_DELAY_PROBE_TIME_UPDATE:
969 p = ptr;
970
971 /* We don't care about changes in the default table. */
972 if (!p->dev || p->tbl != &arp_tbl)
973 return NOTIFY_DONE;
974
975 /* We are in atomic context and can't take RTNL mutex,
976 * so use RCU variant to walk the device chain.
977 */
978 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(p->dev);
979 if (!mlxsw_sp_port)
980 return NOTIFY_DONE;
981
982 mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
983 interval = jiffies_to_msecs(NEIGH_VAR(p, DELAY_PROBE_TIME));
984 mlxsw_sp->router.neighs_update.interval = interval;
985
986 mlxsw_sp_port_dev_put(mlxsw_sp_port);
987 break;
a6bf9e93
YG
988 case NETEVENT_NEIGH_UPDATE:
989 n = ptr;
a6bf9e93
YG
990
991 if (n->tbl != &arp_tbl)
992 return NOTIFY_DONE;
993
5c8802f1 994 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(n->dev);
a6bf9e93
YG
995 if (!mlxsw_sp_port)
996 return NOTIFY_DONE;
997
5c8802f1
IS
998 neigh_work = kzalloc(sizeof(*neigh_work), GFP_ATOMIC);
999 if (!neigh_work) {
a6bf9e93 1000 mlxsw_sp_port_dev_put(mlxsw_sp_port);
5c8802f1 1001 return NOTIFY_BAD;
a6bf9e93 1002 }
5c8802f1
IS
1003
1004 INIT_WORK(&neigh_work->work, mlxsw_sp_router_neigh_event_work);
1005 neigh_work->mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1006 neigh_work->n = n;
a6bf9e93
YG
1007
1008 /* Take a reference to ensure the neighbour won't be
1009 * destructed until we drop the reference in delayed
1010 * work.
1011 */
1012 neigh_clone(n);
5c8802f1
IS
1013 mlxsw_core_schedule_work(&neigh_work->work);
1014 mlxsw_sp_port_dev_put(mlxsw_sp_port);
a6bf9e93 1015 break;
c723c735
YG
1016 }
1017
1018 return NOTIFY_DONE;
1019}
1020
6cf3c971
JP
1021static int mlxsw_sp_neigh_init(struct mlxsw_sp *mlxsw_sp)
1022{
c723c735
YG
1023 int err;
1024
1025 err = rhashtable_init(&mlxsw_sp->router.neigh_ht,
1026 &mlxsw_sp_neigh_ht_params);
1027 if (err)
1028 return err;
1029
1030 /* Initialize the polling interval according to the default
1031 * table.
1032 */
1033 mlxsw_sp_router_neighs_update_interval_init(mlxsw_sp);
1034
0b2361d9 1035 /* Create the delayed works for the activity_update */
c723c735
YG
1036 INIT_DELAYED_WORK(&mlxsw_sp->router.neighs_update.dw,
1037 mlxsw_sp_router_neighs_update_work);
0b2361d9
YG
1038 INIT_DELAYED_WORK(&mlxsw_sp->router.nexthop_probe_dw,
1039 mlxsw_sp_router_probe_unresolved_nexthops);
c723c735 1040 mlxsw_core_schedule_dw(&mlxsw_sp->router.neighs_update.dw, 0);
0b2361d9 1041 mlxsw_core_schedule_dw(&mlxsw_sp->router.nexthop_probe_dw, 0);
c723c735 1042 return 0;
6cf3c971
JP
1043}
1044
1045static void mlxsw_sp_neigh_fini(struct mlxsw_sp *mlxsw_sp)
1046{
c723c735 1047 cancel_delayed_work_sync(&mlxsw_sp->router.neighs_update.dw);
0b2361d9 1048 cancel_delayed_work_sync(&mlxsw_sp->router.nexthop_probe_dw);
6cf3c971
JP
1049 rhashtable_destroy(&mlxsw_sp->router.neigh_ht);
1050}
1051
9665b745
IS
1052static int mlxsw_sp_neigh_rif_flush(struct mlxsw_sp *mlxsw_sp,
1053 const struct mlxsw_sp_rif *r)
1054{
1055 char rauht_pl[MLXSW_REG_RAUHT_LEN];
1056
1057 mlxsw_reg_rauht_pack(rauht_pl, MLXSW_REG_RAUHT_OP_WRITE_DELETE_ALL,
1058 r->rif, r->addr);
1059 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
1060}
1061
1062static void mlxsw_sp_neigh_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
1063 struct mlxsw_sp_rif *r)
1064{
1065 struct mlxsw_sp_neigh_entry *neigh_entry, *tmp;
1066
1067 mlxsw_sp_neigh_rif_flush(mlxsw_sp, r);
1068 list_for_each_entry_safe(neigh_entry, tmp, &r->neigh_list,
1069 rif_list_node)
1070 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
1071}
1072
c53b8e1b
IS
1073struct mlxsw_sp_nexthop_key {
1074 struct fib_nh *fib_nh;
1075};
1076
a7ff87ac
JP
1077struct mlxsw_sp_nexthop {
1078 struct list_head neigh_list_node; /* member of neigh entry list */
9665b745 1079 struct list_head rif_list_node;
a7ff87ac
JP
1080 struct mlxsw_sp_nexthop_group *nh_grp; /* pointer back to the group
1081 * this belongs to
1082 */
c53b8e1b
IS
1083 struct rhash_head ht_node;
1084 struct mlxsw_sp_nexthop_key key;
b8399a1e 1085 struct mlxsw_sp_rif *r;
a7ff87ac
JP
1086 u8 should_offload:1, /* set indicates this neigh is connected and
1087 * should be put to KVD linear area of this group.
1088 */
1089 offloaded:1, /* set in case the neigh is actually put into
1090 * KVD linear area of this group.
1091 */
1092 update:1; /* set indicates that MAC of this neigh should be
1093 * updated in HW
1094 */
1095 struct mlxsw_sp_neigh_entry *neigh_entry;
1096};
1097
e9ad5e7d
IS
1098struct mlxsw_sp_nexthop_group_key {
1099 struct fib_info *fi;
1100};
1101
a7ff87ac 1102struct mlxsw_sp_nexthop_group {
e9ad5e7d 1103 struct rhash_head ht_node;
a7ff87ac 1104 struct list_head fib_list; /* list of fib entries that use this group */
e9ad5e7d 1105 struct mlxsw_sp_nexthop_group_key key;
b3e8d1eb
IS
1106 u8 adj_index_valid:1,
1107 gateway:1; /* routes using the group use a gateway */
a7ff87ac
JP
1108 u32 adj_index;
1109 u16 ecmp_size;
1110 u16 count;
1111 struct mlxsw_sp_nexthop nexthops[0];
b8399a1e 1112#define nh_rif nexthops[0].r
a7ff87ac
JP
1113};
1114
e9ad5e7d
IS
1115static const struct rhashtable_params mlxsw_sp_nexthop_group_ht_params = {
1116 .key_offset = offsetof(struct mlxsw_sp_nexthop_group, key),
1117 .head_offset = offsetof(struct mlxsw_sp_nexthop_group, ht_node),
1118 .key_len = sizeof(struct mlxsw_sp_nexthop_group_key),
1119};
1120
1121static int mlxsw_sp_nexthop_group_insert(struct mlxsw_sp *mlxsw_sp,
1122 struct mlxsw_sp_nexthop_group *nh_grp)
1123{
1124 return rhashtable_insert_fast(&mlxsw_sp->router.nexthop_group_ht,
1125 &nh_grp->ht_node,
1126 mlxsw_sp_nexthop_group_ht_params);
1127}
1128
1129static void mlxsw_sp_nexthop_group_remove(struct mlxsw_sp *mlxsw_sp,
1130 struct mlxsw_sp_nexthop_group *nh_grp)
1131{
1132 rhashtable_remove_fast(&mlxsw_sp->router.nexthop_group_ht,
1133 &nh_grp->ht_node,
1134 mlxsw_sp_nexthop_group_ht_params);
1135}
1136
1137static struct mlxsw_sp_nexthop_group *
1138mlxsw_sp_nexthop_group_lookup(struct mlxsw_sp *mlxsw_sp,
1139 struct mlxsw_sp_nexthop_group_key key)
1140{
1141 return rhashtable_lookup_fast(&mlxsw_sp->router.nexthop_group_ht, &key,
1142 mlxsw_sp_nexthop_group_ht_params);
1143}
1144
c53b8e1b
IS
1145static const struct rhashtable_params mlxsw_sp_nexthop_ht_params = {
1146 .key_offset = offsetof(struct mlxsw_sp_nexthop, key),
1147 .head_offset = offsetof(struct mlxsw_sp_nexthop, ht_node),
1148 .key_len = sizeof(struct mlxsw_sp_nexthop_key),
1149};
1150
1151static int mlxsw_sp_nexthop_insert(struct mlxsw_sp *mlxsw_sp,
1152 struct mlxsw_sp_nexthop *nh)
1153{
1154 return rhashtable_insert_fast(&mlxsw_sp->router.nexthop_ht,
1155 &nh->ht_node, mlxsw_sp_nexthop_ht_params);
1156}
1157
1158static void mlxsw_sp_nexthop_remove(struct mlxsw_sp *mlxsw_sp,
1159 struct mlxsw_sp_nexthop *nh)
1160{
1161 rhashtable_remove_fast(&mlxsw_sp->router.nexthop_ht, &nh->ht_node,
1162 mlxsw_sp_nexthop_ht_params);
1163}
1164
ad178c8e
IS
1165static struct mlxsw_sp_nexthop *
1166mlxsw_sp_nexthop_lookup(struct mlxsw_sp *mlxsw_sp,
1167 struct mlxsw_sp_nexthop_key key)
1168{
1169 return rhashtable_lookup_fast(&mlxsw_sp->router.nexthop_ht, &key,
1170 mlxsw_sp_nexthop_ht_params);
1171}
1172
a7ff87ac
JP
1173static int mlxsw_sp_adj_index_mass_update_vr(struct mlxsw_sp *mlxsw_sp,
1174 struct mlxsw_sp_vr *vr,
1175 u32 adj_index, u16 ecmp_size,
1176 u32 new_adj_index,
1177 u16 new_ecmp_size)
1178{
1179 char raleu_pl[MLXSW_REG_RALEU_LEN];
1180
1a9234e6
IS
1181 mlxsw_reg_raleu_pack(raleu_pl,
1182 (enum mlxsw_reg_ralxx_protocol) vr->proto, vr->id,
1183 adj_index, ecmp_size, new_adj_index,
1184 new_ecmp_size);
a7ff87ac
JP
1185 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raleu), raleu_pl);
1186}
1187
1188static int mlxsw_sp_adj_index_mass_update(struct mlxsw_sp *mlxsw_sp,
1189 struct mlxsw_sp_nexthop_group *nh_grp,
1190 u32 old_adj_index, u16 old_ecmp_size)
1191{
1192 struct mlxsw_sp_fib_entry *fib_entry;
1193 struct mlxsw_sp_vr *vr = NULL;
1194 int err;
1195
1196 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
9aecce1c 1197 if (vr == fib_entry->fib_node->vr)
a7ff87ac 1198 continue;
9aecce1c 1199 vr = fib_entry->fib_node->vr;
a7ff87ac
JP
1200 err = mlxsw_sp_adj_index_mass_update_vr(mlxsw_sp, vr,
1201 old_adj_index,
1202 old_ecmp_size,
1203 nh_grp->adj_index,
1204 nh_grp->ecmp_size);
1205 if (err)
1206 return err;
1207 }
1208 return 0;
1209}
1210
1211static int mlxsw_sp_nexthop_mac_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index,
1212 struct mlxsw_sp_nexthop *nh)
1213{
1214 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
1215 char ratr_pl[MLXSW_REG_RATR_LEN];
1216
1217 mlxsw_reg_ratr_pack(ratr_pl, MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY,
1218 true, adj_index, neigh_entry->rif);
1219 mlxsw_reg_ratr_eth_entry_pack(ratr_pl, neigh_entry->ha);
1220 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl);
1221}
1222
1223static int
1224mlxsw_sp_nexthop_group_mac_update(struct mlxsw_sp *mlxsw_sp,
a59b7e02
IS
1225 struct mlxsw_sp_nexthop_group *nh_grp,
1226 bool reallocate)
a7ff87ac
JP
1227{
1228 u32 adj_index = nh_grp->adj_index; /* base */
1229 struct mlxsw_sp_nexthop *nh;
1230 int i;
1231 int err;
1232
1233 for (i = 0; i < nh_grp->count; i++) {
1234 nh = &nh_grp->nexthops[i];
1235
1236 if (!nh->should_offload) {
1237 nh->offloaded = 0;
1238 continue;
1239 }
1240
a59b7e02 1241 if (nh->update || reallocate) {
a7ff87ac
JP
1242 err = mlxsw_sp_nexthop_mac_update(mlxsw_sp,
1243 adj_index, nh);
1244 if (err)
1245 return err;
1246 nh->update = 0;
1247 nh->offloaded = 1;
1248 }
1249 adj_index++;
1250 }
1251 return 0;
1252}
1253
1254static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
1255 struct mlxsw_sp_fib_entry *fib_entry);
1256
1257static int
1258mlxsw_sp_nexthop_fib_entries_update(struct mlxsw_sp *mlxsw_sp,
1259 struct mlxsw_sp_nexthop_group *nh_grp)
1260{
1261 struct mlxsw_sp_fib_entry *fib_entry;
1262 int err;
1263
1264 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
1265 err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
1266 if (err)
1267 return err;
1268 }
1269 return 0;
1270}
1271
1272static void
1273mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp,
1274 struct mlxsw_sp_nexthop_group *nh_grp)
1275{
1276 struct mlxsw_sp_nexthop *nh;
1277 bool offload_change = false;
1278 u32 adj_index;
1279 u16 ecmp_size = 0;
1280 bool old_adj_index_valid;
1281 u32 old_adj_index;
1282 u16 old_ecmp_size;
1283 int ret;
1284 int i;
1285 int err;
1286
b3e8d1eb
IS
1287 if (!nh_grp->gateway) {
1288 mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
1289 return;
1290 }
1291
a7ff87ac
JP
1292 for (i = 0; i < nh_grp->count; i++) {
1293 nh = &nh_grp->nexthops[i];
1294
1295 if (nh->should_offload ^ nh->offloaded) {
1296 offload_change = true;
1297 if (nh->should_offload)
1298 nh->update = 1;
1299 }
1300 if (nh->should_offload)
1301 ecmp_size++;
1302 }
1303 if (!offload_change) {
1304 /* Nothing was added or removed, so no need to reallocate. Just
1305 * update MAC on existing adjacency indexes.
1306 */
a59b7e02
IS
1307 err = mlxsw_sp_nexthop_group_mac_update(mlxsw_sp, nh_grp,
1308 false);
a7ff87ac
JP
1309 if (err) {
1310 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
1311 goto set_trap;
1312 }
1313 return;
1314 }
1315 if (!ecmp_size)
1316 /* No neigh of this group is connected so we just set
1317 * the trap and let everthing flow through kernel.
1318 */
1319 goto set_trap;
1320
1321 ret = mlxsw_sp_kvdl_alloc(mlxsw_sp, ecmp_size);
1322 if (ret < 0) {
1323 /* We ran out of KVD linear space, just set the
1324 * trap and let everything flow through kernel.
1325 */
1326 dev_warn(mlxsw_sp->bus_info->dev, "Failed to allocate KVD linear area for nexthop group.\n");
1327 goto set_trap;
1328 }
1329 adj_index = ret;
1330 old_adj_index_valid = nh_grp->adj_index_valid;
1331 old_adj_index = nh_grp->adj_index;
1332 old_ecmp_size = nh_grp->ecmp_size;
1333 nh_grp->adj_index_valid = 1;
1334 nh_grp->adj_index = adj_index;
1335 nh_grp->ecmp_size = ecmp_size;
a59b7e02 1336 err = mlxsw_sp_nexthop_group_mac_update(mlxsw_sp, nh_grp, true);
a7ff87ac
JP
1337 if (err) {
1338 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
1339 goto set_trap;
1340 }
1341
1342 if (!old_adj_index_valid) {
1343 /* The trap was set for fib entries, so we have to call
1344 * fib entry update to unset it and use adjacency index.
1345 */
1346 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
1347 if (err) {
1348 dev_warn(mlxsw_sp->bus_info->dev, "Failed to add adjacency index to fib entries.\n");
1349 goto set_trap;
1350 }
1351 return;
1352 }
1353
1354 err = mlxsw_sp_adj_index_mass_update(mlxsw_sp, nh_grp,
1355 old_adj_index, old_ecmp_size);
1356 mlxsw_sp_kvdl_free(mlxsw_sp, old_adj_index);
1357 if (err) {
1358 dev_warn(mlxsw_sp->bus_info->dev, "Failed to mass-update adjacency index for nexthop group.\n");
1359 goto set_trap;
1360 }
1361 return;
1362
1363set_trap:
1364 old_adj_index_valid = nh_grp->adj_index_valid;
1365 nh_grp->adj_index_valid = 0;
1366 for (i = 0; i < nh_grp->count; i++) {
1367 nh = &nh_grp->nexthops[i];
1368 nh->offloaded = 0;
1369 }
1370 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
1371 if (err)
1372 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set traps for fib entries.\n");
1373 if (old_adj_index_valid)
1374 mlxsw_sp_kvdl_free(mlxsw_sp, nh_grp->adj_index);
1375}
1376
1377static void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh,
1378 bool removing)
1379{
1380 if (!removing && !nh->should_offload)
1381 nh->should_offload = 1;
1382 else if (removing && nh->offloaded)
1383 nh->should_offload = 0;
1384 nh->update = 1;
1385}
1386
1387static void
1388mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
1389 struct mlxsw_sp_neigh_entry *neigh_entry,
1390 bool removing)
1391{
1392 struct mlxsw_sp_nexthop *nh;
1393
a7ff87ac
JP
1394 list_for_each_entry(nh, &neigh_entry->nexthop_list,
1395 neigh_list_node) {
1396 __mlxsw_sp_nexthop_neigh_update(nh, removing);
1397 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
1398 }
a7ff87ac
JP
1399}
1400
9665b745
IS
1401static void mlxsw_sp_nexthop_rif_init(struct mlxsw_sp_nexthop *nh,
1402 struct mlxsw_sp_rif *r)
1403{
1404 if (nh->r)
1405 return;
1406
1407 nh->r = r;
1408 list_add(&nh->rif_list_node, &r->nexthop_list);
1409}
1410
1411static void mlxsw_sp_nexthop_rif_fini(struct mlxsw_sp_nexthop *nh)
1412{
1413 if (!nh->r)
1414 return;
1415
1416 list_del(&nh->rif_list_node);
1417 nh->r = NULL;
1418}
1419
a8c97014
IS
1420static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp,
1421 struct mlxsw_sp_nexthop *nh)
a7ff87ac
JP
1422{
1423 struct mlxsw_sp_neigh_entry *neigh_entry;
a8c97014 1424 struct fib_nh *fib_nh = nh->key.fib_nh;
a7ff87ac 1425 struct neighbour *n;
93a87e5e 1426 u8 nud_state, dead;
c53b8e1b
IS
1427 int err;
1428
ad178c8e 1429 if (!nh->nh_grp->gateway || nh->neigh_entry)
b8399a1e
IS
1430 return 0;
1431
33b1341c
JP
1432 /* Take a reference of neigh here ensuring that neigh would
1433 * not be detructed before the nexthop entry is finished.
1434 * The reference is taken either in neigh_lookup() or
fd76d910 1435 * in neigh_create() in case n is not found.
33b1341c 1436 */
a8c97014 1437 n = neigh_lookup(&arp_tbl, &fib_nh->nh_gw, fib_nh->nh_dev);
33b1341c 1438 if (!n) {
a8c97014
IS
1439 n = neigh_create(&arp_tbl, &fib_nh->nh_gw, fib_nh->nh_dev);
1440 if (IS_ERR(n))
1441 return PTR_ERR(n);
a7ff87ac 1442 neigh_event_send(n, NULL);
33b1341c
JP
1443 }
1444 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
1445 if (!neigh_entry) {
5c8802f1
IS
1446 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
1447 if (IS_ERR(neigh_entry)) {
c53b8e1b
IS
1448 err = -EINVAL;
1449 goto err_neigh_entry_create;
5c8802f1 1450 }
a7ff87ac 1451 }
b2157149
YG
1452
1453 /* If that is the first nexthop connected to that neigh, add to
1454 * nexthop_neighs_list
1455 */
1456 if (list_empty(&neigh_entry->nexthop_list))
1457 list_add_tail(&neigh_entry->nexthop_neighs_list_node,
1458 &mlxsw_sp->router.nexthop_neighs_list);
1459
a7ff87ac
JP
1460 nh->neigh_entry = neigh_entry;
1461 list_add_tail(&nh->neigh_list_node, &neigh_entry->nexthop_list);
1462 read_lock_bh(&n->lock);
1463 nud_state = n->nud_state;
93a87e5e 1464 dead = n->dead;
a7ff87ac 1465 read_unlock_bh(&n->lock);
93a87e5e 1466 __mlxsw_sp_nexthop_neigh_update(nh, !(nud_state & NUD_VALID && !dead));
a7ff87ac
JP
1467
1468 return 0;
c53b8e1b
IS
1469
1470err_neigh_entry_create:
1471 neigh_release(n);
c53b8e1b 1472 return err;
a7ff87ac
JP
1473}
1474
a8c97014
IS
1475static void mlxsw_sp_nexthop_neigh_fini(struct mlxsw_sp *mlxsw_sp,
1476 struct mlxsw_sp_nexthop *nh)
a7ff87ac
JP
1477{
1478 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
a8c97014 1479 struct neighbour *n;
a7ff87ac 1480
b8399a1e 1481 if (!neigh_entry)
a8c97014
IS
1482 return;
1483 n = neigh_entry->key.n;
b8399a1e 1484
58312125 1485 __mlxsw_sp_nexthop_neigh_update(nh, true);
a7ff87ac 1486 list_del(&nh->neigh_list_node);
e58be79e 1487 nh->neigh_entry = NULL;
b2157149
YG
1488
1489 /* If that is the last nexthop connected to that neigh, remove from
1490 * nexthop_neighs_list
1491 */
e58be79e
IS
1492 if (list_empty(&neigh_entry->nexthop_list))
1493 list_del(&neigh_entry->nexthop_neighs_list_node);
b2157149 1494
5c8802f1
IS
1495 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
1496 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
1497
1498 neigh_release(n);
a8c97014 1499}
c53b8e1b 1500
a8c97014
IS
1501static int mlxsw_sp_nexthop_init(struct mlxsw_sp *mlxsw_sp,
1502 struct mlxsw_sp_nexthop_group *nh_grp,
1503 struct mlxsw_sp_nexthop *nh,
1504 struct fib_nh *fib_nh)
1505{
1506 struct net_device *dev = fib_nh->nh_dev;
df6dd79b 1507 struct in_device *in_dev;
a8c97014
IS
1508 struct mlxsw_sp_rif *r;
1509 int err;
1510
1511 nh->nh_grp = nh_grp;
1512 nh->key.fib_nh = fib_nh;
1513 err = mlxsw_sp_nexthop_insert(mlxsw_sp, nh);
1514 if (err)
1515 return err;
1516
97989ee0
IS
1517 if (!dev)
1518 return 0;
1519
df6dd79b
IS
1520 in_dev = __in_dev_get_rtnl(dev);
1521 if (in_dev && IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) &&
1522 fib_nh->nh_flags & RTNH_F_LINKDOWN)
1523 return 0;
1524
a8c97014
IS
1525 r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
1526 if (!r)
1527 return 0;
9665b745 1528 mlxsw_sp_nexthop_rif_init(nh, r);
a8c97014
IS
1529
1530 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
1531 if (err)
1532 goto err_nexthop_neigh_init;
1533
1534 return 0;
1535
1536err_nexthop_neigh_init:
1537 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
1538 return err;
1539}
1540
1541static void mlxsw_sp_nexthop_fini(struct mlxsw_sp *mlxsw_sp,
1542 struct mlxsw_sp_nexthop *nh)
1543{
1544 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
9665b745 1545 mlxsw_sp_nexthop_rif_fini(nh);
c53b8e1b 1546 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
a7ff87ac
JP
1547}
1548
ad178c8e
IS
1549static void mlxsw_sp_nexthop_event(struct mlxsw_sp *mlxsw_sp,
1550 unsigned long event, struct fib_nh *fib_nh)
1551{
1552 struct mlxsw_sp_nexthop_key key;
1553 struct mlxsw_sp_nexthop *nh;
1554 struct mlxsw_sp_rif *r;
1555
1556 if (mlxsw_sp->router.aborted)
1557 return;
1558
1559 key.fib_nh = fib_nh;
1560 nh = mlxsw_sp_nexthop_lookup(mlxsw_sp, key);
1561 if (WARN_ON_ONCE(!nh))
1562 return;
1563
1564 r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, fib_nh->nh_dev);
1565 if (!r)
1566 return;
1567
1568 switch (event) {
1569 case FIB_EVENT_NH_ADD:
9665b745 1570 mlxsw_sp_nexthop_rif_init(nh, r);
ad178c8e
IS
1571 mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
1572 break;
1573 case FIB_EVENT_NH_DEL:
1574 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
9665b745 1575 mlxsw_sp_nexthop_rif_fini(nh);
ad178c8e
IS
1576 break;
1577 }
1578
1579 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
1580}
1581
9665b745
IS
1582static void mlxsw_sp_nexthop_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
1583 struct mlxsw_sp_rif *r)
1584{
1585 struct mlxsw_sp_nexthop *nh, *tmp;
1586
1587 list_for_each_entry_safe(nh, tmp, &r->nexthop_list, rif_list_node) {
1588 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
1589 mlxsw_sp_nexthop_rif_fini(nh);
1590 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
1591 }
1592}
1593
a7ff87ac
JP
1594static struct mlxsw_sp_nexthop_group *
1595mlxsw_sp_nexthop_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi)
1596{
1597 struct mlxsw_sp_nexthop_group *nh_grp;
1598 struct mlxsw_sp_nexthop *nh;
1599 struct fib_nh *fib_nh;
1600 size_t alloc_size;
1601 int i;
1602 int err;
1603
1604 alloc_size = sizeof(*nh_grp) +
1605 fi->fib_nhs * sizeof(struct mlxsw_sp_nexthop);
1606 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
1607 if (!nh_grp)
1608 return ERR_PTR(-ENOMEM);
1609 INIT_LIST_HEAD(&nh_grp->fib_list);
b3e8d1eb 1610 nh_grp->gateway = fi->fib_nh->nh_scope == RT_SCOPE_LINK;
a7ff87ac 1611 nh_grp->count = fi->fib_nhs;
e9ad5e7d 1612 nh_grp->key.fi = fi;
a7ff87ac
JP
1613 for (i = 0; i < nh_grp->count; i++) {
1614 nh = &nh_grp->nexthops[i];
1615 fib_nh = &fi->fib_nh[i];
1616 err = mlxsw_sp_nexthop_init(mlxsw_sp, nh_grp, nh, fib_nh);
1617 if (err)
1618 goto err_nexthop_init;
1619 }
e9ad5e7d
IS
1620 err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
1621 if (err)
1622 goto err_nexthop_group_insert;
a7ff87ac
JP
1623 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
1624 return nh_grp;
1625
e9ad5e7d 1626err_nexthop_group_insert:
a7ff87ac 1627err_nexthop_init:
df6dd79b
IS
1628 for (i--; i >= 0; i--) {
1629 nh = &nh_grp->nexthops[i];
a7ff87ac 1630 mlxsw_sp_nexthop_fini(mlxsw_sp, nh);
df6dd79b 1631 }
a7ff87ac
JP
1632 kfree(nh_grp);
1633 return ERR_PTR(err);
1634}
1635
1636static void
1637mlxsw_sp_nexthop_group_destroy(struct mlxsw_sp *mlxsw_sp,
1638 struct mlxsw_sp_nexthop_group *nh_grp)
1639{
1640 struct mlxsw_sp_nexthop *nh;
1641 int i;
1642
e9ad5e7d 1643 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
a7ff87ac
JP
1644 for (i = 0; i < nh_grp->count; i++) {
1645 nh = &nh_grp->nexthops[i];
1646 mlxsw_sp_nexthop_fini(mlxsw_sp, nh);
1647 }
58312125
IS
1648 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
1649 WARN_ON_ONCE(nh_grp->adj_index_valid);
a7ff87ac
JP
1650 kfree(nh_grp);
1651}
1652
a7ff87ac
JP
1653static int mlxsw_sp_nexthop_group_get(struct mlxsw_sp *mlxsw_sp,
1654 struct mlxsw_sp_fib_entry *fib_entry,
1655 struct fib_info *fi)
1656{
e9ad5e7d 1657 struct mlxsw_sp_nexthop_group_key key;
a7ff87ac
JP
1658 struct mlxsw_sp_nexthop_group *nh_grp;
1659
e9ad5e7d
IS
1660 key.fi = fi;
1661 nh_grp = mlxsw_sp_nexthop_group_lookup(mlxsw_sp, key);
a7ff87ac
JP
1662 if (!nh_grp) {
1663 nh_grp = mlxsw_sp_nexthop_group_create(mlxsw_sp, fi);
1664 if (IS_ERR(nh_grp))
1665 return PTR_ERR(nh_grp);
1666 }
1667 list_add_tail(&fib_entry->nexthop_group_node, &nh_grp->fib_list);
1668 fib_entry->nh_group = nh_grp;
1669 return 0;
1670}
1671
1672static void mlxsw_sp_nexthop_group_put(struct mlxsw_sp *mlxsw_sp,
1673 struct mlxsw_sp_fib_entry *fib_entry)
1674{
1675 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
1676
1677 list_del(&fib_entry->nexthop_group_node);
1678 if (!list_empty(&nh_grp->fib_list))
1679 return;
1680 mlxsw_sp_nexthop_group_destroy(mlxsw_sp, nh_grp);
1681}
1682
013b20f9
IS
1683static bool
1684mlxsw_sp_fib_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
1685{
1686 struct mlxsw_sp_nexthop_group *nh_group = fib_entry->nh_group;
1687
9aecce1c
IS
1688 if (fib_entry->params.tos)
1689 return false;
1690
013b20f9
IS
1691 switch (fib_entry->type) {
1692 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
1693 return !!nh_group->adj_index_valid;
1694 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
70ad3506 1695 return !!nh_group->nh_rif;
013b20f9
IS
1696 default:
1697 return false;
1698 }
1699}
1700
1701static void mlxsw_sp_fib_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
1702{
1703 fib_entry->offloaded = true;
1704
9aecce1c 1705 switch (fib_entry->fib_node->vr->proto) {
013b20f9
IS
1706 case MLXSW_SP_L3_PROTO_IPV4:
1707 fib_info_offload_inc(fib_entry->nh_group->key.fi);
1708 break;
1709 case MLXSW_SP_L3_PROTO_IPV6:
1710 WARN_ON_ONCE(1);
1711 }
1712}
1713
1714static void
1715mlxsw_sp_fib_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
1716{
9aecce1c 1717 switch (fib_entry->fib_node->vr->proto) {
013b20f9
IS
1718 case MLXSW_SP_L3_PROTO_IPV4:
1719 fib_info_offload_dec(fib_entry->nh_group->key.fi);
1720 break;
1721 case MLXSW_SP_L3_PROTO_IPV6:
1722 WARN_ON_ONCE(1);
1723 }
1724
1725 fib_entry->offloaded = false;
1726}
1727
1728static void
1729mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
1730 enum mlxsw_reg_ralue_op op, int err)
1731{
1732 switch (op) {
1733 case MLXSW_REG_RALUE_OP_WRITE_DELETE:
1734 if (!fib_entry->offloaded)
1735 return;
1736 return mlxsw_sp_fib_entry_offload_unset(fib_entry);
1737 case MLXSW_REG_RALUE_OP_WRITE_WRITE:
1738 if (err)
1739 return;
1740 if (mlxsw_sp_fib_entry_should_offload(fib_entry) &&
1741 !fib_entry->offloaded)
1742 mlxsw_sp_fib_entry_offload_set(fib_entry);
1743 else if (!mlxsw_sp_fib_entry_should_offload(fib_entry) &&
1744 fib_entry->offloaded)
1745 mlxsw_sp_fib_entry_offload_unset(fib_entry);
1746 return;
1747 default:
1748 return;
1749 }
1750}
1751
a7ff87ac
JP
1752static int mlxsw_sp_fib_entry_op4_remote(struct mlxsw_sp *mlxsw_sp,
1753 struct mlxsw_sp_fib_entry *fib_entry,
1754 enum mlxsw_reg_ralue_op op)
1755{
1756 char ralue_pl[MLXSW_REG_RALUE_LEN];
9aecce1c
IS
1757 u32 *p_dip = (u32 *) fib_entry->fib_node->key.addr;
1758 struct mlxsw_sp_vr *vr = fib_entry->fib_node->vr;
a7ff87ac
JP
1759 enum mlxsw_reg_ralue_trap_action trap_action;
1760 u16 trap_id = 0;
1761 u32 adjacency_index = 0;
1762 u16 ecmp_size = 0;
1763
1764 /* In case the nexthop group adjacency index is valid, use it
1765 * with provided ECMP size. Otherwise, setup trap and pass
1766 * traffic to kernel.
1767 */
4b411477 1768 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
a7ff87ac
JP
1769 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
1770 adjacency_index = fib_entry->nh_group->adj_index;
1771 ecmp_size = fib_entry->nh_group->ecmp_size;
1772 } else {
1773 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
1774 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
1775 }
1776
1a9234e6
IS
1777 mlxsw_reg_ralue_pack4(ralue_pl,
1778 (enum mlxsw_reg_ralxx_protocol) vr->proto, op,
9aecce1c
IS
1779 vr->id, fib_entry->fib_node->key.prefix_len,
1780 *p_dip);
a7ff87ac
JP
1781 mlxsw_reg_ralue_act_remote_pack(ralue_pl, trap_action, trap_id,
1782 adjacency_index, ecmp_size);
1783 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
1784}
1785
61c503f9
JP
1786static int mlxsw_sp_fib_entry_op4_local(struct mlxsw_sp *mlxsw_sp,
1787 struct mlxsw_sp_fib_entry *fib_entry,
1788 enum mlxsw_reg_ralue_op op)
1789{
b8399a1e 1790 struct mlxsw_sp_rif *r = fib_entry->nh_group->nh_rif;
70ad3506 1791 enum mlxsw_reg_ralue_trap_action trap_action;
61c503f9 1792 char ralue_pl[MLXSW_REG_RALUE_LEN];
9aecce1c
IS
1793 u32 *p_dip = (u32 *) fib_entry->fib_node->key.addr;
1794 struct mlxsw_sp_vr *vr = fib_entry->fib_node->vr;
70ad3506
IS
1795 u16 trap_id = 0;
1796 u16 rif = 0;
1797
1798 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
1799 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
1800 rif = r->rif;
1801 } else {
1802 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
1803 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
1804 }
61c503f9 1805
1a9234e6
IS
1806 mlxsw_reg_ralue_pack4(ralue_pl,
1807 (enum mlxsw_reg_ralxx_protocol) vr->proto, op,
9aecce1c
IS
1808 vr->id, fib_entry->fib_node->key.prefix_len,
1809 *p_dip);
70ad3506 1810 mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, trap_id, rif);
61c503f9
JP
1811 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
1812}
1813
1814static int mlxsw_sp_fib_entry_op4_trap(struct mlxsw_sp *mlxsw_sp,
1815 struct mlxsw_sp_fib_entry *fib_entry,
1816 enum mlxsw_reg_ralue_op op)
1817{
1818 char ralue_pl[MLXSW_REG_RALUE_LEN];
9aecce1c
IS
1819 u32 *p_dip = (u32 *) fib_entry->fib_node->key.addr;
1820 struct mlxsw_sp_vr *vr = fib_entry->fib_node->vr;
61c503f9 1821
1a9234e6
IS
1822 mlxsw_reg_ralue_pack4(ralue_pl,
1823 (enum mlxsw_reg_ralxx_protocol) vr->proto, op,
9aecce1c
IS
1824 vr->id, fib_entry->fib_node->key.prefix_len,
1825 *p_dip);
61c503f9
JP
1826 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
1827 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
1828}
1829
1830static int mlxsw_sp_fib_entry_op4(struct mlxsw_sp *mlxsw_sp,
1831 struct mlxsw_sp_fib_entry *fib_entry,
1832 enum mlxsw_reg_ralue_op op)
1833{
1834 switch (fib_entry->type) {
1835 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
a7ff87ac 1836 return mlxsw_sp_fib_entry_op4_remote(mlxsw_sp, fib_entry, op);
61c503f9
JP
1837 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
1838 return mlxsw_sp_fib_entry_op4_local(mlxsw_sp, fib_entry, op);
1839 case MLXSW_SP_FIB_ENTRY_TYPE_TRAP:
1840 return mlxsw_sp_fib_entry_op4_trap(mlxsw_sp, fib_entry, op);
1841 }
1842 return -EINVAL;
1843}
1844
1845static int mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
1846 struct mlxsw_sp_fib_entry *fib_entry,
1847 enum mlxsw_reg_ralue_op op)
1848{
013b20f9
IS
1849 int err = -EINVAL;
1850
9aecce1c 1851 switch (fib_entry->fib_node->vr->proto) {
61c503f9 1852 case MLXSW_SP_L3_PROTO_IPV4:
013b20f9
IS
1853 err = mlxsw_sp_fib_entry_op4(mlxsw_sp, fib_entry, op);
1854 break;
61c503f9 1855 case MLXSW_SP_L3_PROTO_IPV6:
013b20f9 1856 return err;
61c503f9 1857 }
013b20f9
IS
1858 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, err);
1859 return err;
61c503f9
JP
1860}
1861
1862static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
1863 struct mlxsw_sp_fib_entry *fib_entry)
1864{
7146da31
JP
1865 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
1866 MLXSW_REG_RALUE_OP_WRITE_WRITE);
61c503f9
JP
1867}
1868
1869static int mlxsw_sp_fib_entry_del(struct mlxsw_sp *mlxsw_sp,
1870 struct mlxsw_sp_fib_entry *fib_entry)
1871{
1872 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
1873 MLXSW_REG_RALUE_OP_WRITE_DELETE);
1874}
1875
61c503f9 1876static int
013b20f9
IS
1877mlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp,
1878 const struct fib_entry_notifier_info *fen_info,
1879 struct mlxsw_sp_fib_entry *fib_entry)
61c503f9 1880{
b45f64d1 1881 struct fib_info *fi = fen_info->fi;
61c503f9 1882
97989ee0
IS
1883 switch (fen_info->type) {
1884 case RTN_BROADCAST: /* fall through */
1885 case RTN_LOCAL:
61c503f9
JP
1886 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
1887 return 0;
97989ee0
IS
1888 case RTN_UNREACHABLE: /* fall through */
1889 case RTN_BLACKHOLE: /* fall through */
1890 case RTN_PROHIBIT:
1891 /* Packets hitting these routes need to be trapped, but
1892 * can do so with a lower priority than packets directed
1893 * at the host, so use action type local instead of trap.
1894 */
61c503f9 1895 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
97989ee0
IS
1896 return 0;
1897 case RTN_UNICAST:
1898 if (fi->fib_nh->nh_scope != RT_SCOPE_LINK)
1899 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
1900 else
1901 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
1902 return 0;
1903 default:
1904 return -EINVAL;
1905 }
a7ff87ac
JP
1906}
1907
5b004412 1908static struct mlxsw_sp_fib_entry *
9aecce1c
IS
1909mlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp,
1910 struct mlxsw_sp_fib_node *fib_node,
1911 const struct fib_entry_notifier_info *fen_info)
61c503f9 1912{
61c503f9 1913 struct mlxsw_sp_fib_entry *fib_entry;
61c503f9
JP
1914 int err;
1915
9aecce1c 1916 fib_entry = kzalloc(sizeof(*fib_entry), GFP_KERNEL);
61c503f9
JP
1917 if (!fib_entry) {
1918 err = -ENOMEM;
9aecce1c 1919 goto err_fib_entry_alloc;
61c503f9 1920 }
61c503f9 1921
013b20f9 1922 err = mlxsw_sp_fib4_entry_type_set(mlxsw_sp, fen_info, fib_entry);
61c503f9 1923 if (err)
013b20f9 1924 goto err_fib4_entry_type_set;
61c503f9 1925
9aecce1c 1926 err = mlxsw_sp_nexthop_group_get(mlxsw_sp, fib_entry, fen_info->fi);
b8399a1e
IS
1927 if (err)
1928 goto err_nexthop_group_get;
1929
9aecce1c
IS
1930 fib_entry->params.prio = fen_info->fi->fib_priority;
1931 fib_entry->params.tb_id = fen_info->tb_id;
1932 fib_entry->params.type = fen_info->type;
1933 fib_entry->params.tos = fen_info->tos;
1934
1935 fib_entry->fib_node = fib_node;
1936
5b004412
JP
1937 return fib_entry;
1938
b8399a1e 1939err_nexthop_group_get:
013b20f9 1940err_fib4_entry_type_set:
9aecce1c
IS
1941 kfree(fib_entry);
1942err_fib_entry_alloc:
5b004412
JP
1943 return ERR_PTR(err);
1944}
1945
9aecce1c
IS
1946static void mlxsw_sp_fib4_entry_destroy(struct mlxsw_sp *mlxsw_sp,
1947 struct mlxsw_sp_fib_entry *fib_entry)
1948{
1949 mlxsw_sp_nexthop_group_put(mlxsw_sp, fib_entry);
1950 kfree(fib_entry);
1951}
1952
1953static struct mlxsw_sp_fib_node *
1954mlxsw_sp_fib4_node_get(struct mlxsw_sp *mlxsw_sp,
1955 const struct fib_entry_notifier_info *fen_info);
1956
5b004412 1957static struct mlxsw_sp_fib_entry *
9aecce1c
IS
1958mlxsw_sp_fib4_entry_lookup(struct mlxsw_sp *mlxsw_sp,
1959 const struct fib_entry_notifier_info *fen_info)
5b004412 1960{
9aecce1c
IS
1961 struct mlxsw_sp_fib_entry *fib_entry;
1962 struct mlxsw_sp_fib_node *fib_node;
5b004412 1963
9aecce1c
IS
1964 fib_node = mlxsw_sp_fib4_node_get(mlxsw_sp, fen_info);
1965 if (IS_ERR(fib_node))
1966 return NULL;
1967
1968 list_for_each_entry(fib_entry, &fib_node->entry_list, list) {
1969 if (fib_entry->params.tb_id == fen_info->tb_id &&
1970 fib_entry->params.tos == fen_info->tos &&
1971 fib_entry->params.type == fen_info->type &&
1972 fib_entry->nh_group->key.fi == fen_info->fi) {
1973 return fib_entry;
1974 }
1975 }
1976
1977 return NULL;
1978}
1979
1980static const struct rhashtable_params mlxsw_sp_fib_ht_params = {
1981 .key_offset = offsetof(struct mlxsw_sp_fib_node, key),
1982 .head_offset = offsetof(struct mlxsw_sp_fib_node, ht_node),
1983 .key_len = sizeof(struct mlxsw_sp_fib_key),
1984 .automatic_shrinking = true,
1985};
1986
1987static int mlxsw_sp_fib_node_insert(struct mlxsw_sp_fib *fib,
1988 struct mlxsw_sp_fib_node *fib_node)
1989{
1990 return rhashtable_insert_fast(&fib->ht, &fib_node->ht_node,
1991 mlxsw_sp_fib_ht_params);
1992}
1993
1994static void mlxsw_sp_fib_node_remove(struct mlxsw_sp_fib *fib,
1995 struct mlxsw_sp_fib_node *fib_node)
1996{
1997 rhashtable_remove_fast(&fib->ht, &fib_node->ht_node,
1998 mlxsw_sp_fib_ht_params);
1999}
2000
2001static struct mlxsw_sp_fib_node *
2002mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
2003 size_t addr_len, unsigned char prefix_len)
2004{
2005 struct mlxsw_sp_fib_key key;
2006
2007 memset(&key, 0, sizeof(key));
2008 memcpy(key.addr, addr, addr_len);
2009 key.prefix_len = prefix_len;
2010 return rhashtable_lookup_fast(&fib->ht, &key, mlxsw_sp_fib_ht_params);
2011}
2012
2013static struct mlxsw_sp_fib_node *
2014mlxsw_sp_fib_node_create(struct mlxsw_sp_vr *vr, const void *addr,
2015 size_t addr_len, unsigned char prefix_len)
2016{
2017 struct mlxsw_sp_fib_node *fib_node;
2018
2019 fib_node = kzalloc(sizeof(*fib_node), GFP_KERNEL);
2020 if (!fib_node)
5b004412
JP
2021 return NULL;
2022
9aecce1c
IS
2023 INIT_LIST_HEAD(&fib_node->entry_list);
2024 list_add(&fib_node->list, &vr->fib->node_list);
2025 memcpy(fib_node->key.addr, addr, addr_len);
2026 fib_node->key.prefix_len = prefix_len;
2027 mlxsw_sp_fib_node_insert(vr->fib, fib_node);
2028 fib_node->vr = vr;
2029
2030 return fib_node;
2031}
2032
2033static void mlxsw_sp_fib_node_destroy(struct mlxsw_sp_fib_node *fib_node)
2034{
2035 mlxsw_sp_fib_node_remove(fib_node->vr->fib, fib_node);
2036 list_del(&fib_node->list);
2037 WARN_ON(!list_empty(&fib_node->entry_list));
2038 kfree(fib_node);
2039}
2040
2041static bool
2042mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
2043 const struct mlxsw_sp_fib_entry *fib_entry)
2044{
2045 return list_first_entry(&fib_node->entry_list,
2046 struct mlxsw_sp_fib_entry, list) == fib_entry;
2047}
2048
2049static void mlxsw_sp_fib_node_prefix_inc(struct mlxsw_sp_fib_node *fib_node)
2050{
2051 unsigned char prefix_len = fib_node->key.prefix_len;
2052 struct mlxsw_sp_fib *fib = fib_node->vr->fib;
2053
2054 if (fib->prefix_ref_count[prefix_len]++ == 0)
2055 mlxsw_sp_prefix_usage_set(&fib->prefix_usage, prefix_len);
2056}
2057
2058static void mlxsw_sp_fib_node_prefix_dec(struct mlxsw_sp_fib_node *fib_node)
2059{
2060 unsigned char prefix_len = fib_node->key.prefix_len;
2061 struct mlxsw_sp_fib *fib = fib_node->vr->fib;
2062
2063 if (--fib->prefix_ref_count[prefix_len] == 0)
2064 mlxsw_sp_prefix_usage_clear(&fib->prefix_usage, prefix_len);
5b004412
JP
2065}
2066
9aecce1c
IS
2067static struct mlxsw_sp_fib_node *
2068mlxsw_sp_fib4_node_get(struct mlxsw_sp *mlxsw_sp,
2069 const struct fib_entry_notifier_info *fen_info)
5b004412 2070{
9aecce1c
IS
2071 struct mlxsw_sp_fib_node *fib_node;
2072 struct mlxsw_sp_vr *vr;
2073 int err;
2074
2075 vr = mlxsw_sp_vr_get(mlxsw_sp, fen_info->dst_len, fen_info->tb_id,
2076 MLXSW_SP_L3_PROTO_IPV4);
2077 if (IS_ERR(vr))
2078 return ERR_CAST(vr);
2079
2080 fib_node = mlxsw_sp_fib_node_lookup(vr->fib, &fen_info->dst,
2081 sizeof(fen_info->dst),
2082 fen_info->dst_len);
2083 if (fib_node)
2084 return fib_node;
5b004412 2085
9aecce1c
IS
2086 fib_node = mlxsw_sp_fib_node_create(vr, &fen_info->dst,
2087 sizeof(fen_info->dst),
2088 fen_info->dst_len);
2089 if (!fib_node) {
2090 err = -ENOMEM;
2091 goto err_fib_node_create;
5b004412 2092 }
9aecce1c
IS
2093
2094 return fib_node;
2095
2096err_fib_node_create:
5b004412 2097 mlxsw_sp_vr_put(mlxsw_sp, vr);
9aecce1c 2098 return ERR_PTR(err);
5b004412
JP
2099}
2100
9aecce1c
IS
2101static void mlxsw_sp_fib4_node_put(struct mlxsw_sp *mlxsw_sp,
2102 struct mlxsw_sp_fib_node *fib_node)
5b004412 2103{
9aecce1c 2104 struct mlxsw_sp_vr *vr = fib_node->vr;
5b004412 2105
9aecce1c
IS
2106 if (!list_empty(&fib_node->entry_list))
2107 return;
2108 mlxsw_sp_fib_node_destroy(fib_node);
2109 mlxsw_sp_vr_put(mlxsw_sp, vr);
61c503f9
JP
2110}
2111
9aecce1c
IS
2112static struct mlxsw_sp_fib_entry *
2113mlxsw_sp_fib4_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
2114 const struct mlxsw_sp_fib_entry_params *params)
61c503f9 2115{
61c503f9 2116 struct mlxsw_sp_fib_entry *fib_entry;
9aecce1c
IS
2117
2118 list_for_each_entry(fib_entry, &fib_node->entry_list, list) {
2119 if (fib_entry->params.tb_id > params->tb_id)
2120 continue;
2121 if (fib_entry->params.tb_id != params->tb_id)
2122 break;
2123 if (fib_entry->params.tos > params->tos)
2124 continue;
2125 if (fib_entry->params.prio >= params->prio ||
2126 fib_entry->params.tos < params->tos)
2127 return fib_entry;
2128 }
2129
2130 return NULL;
2131}
2132
4283bce5
IS
2133static int mlxsw_sp_fib4_node_list_append(struct mlxsw_sp_fib_entry *fib_entry,
2134 struct mlxsw_sp_fib_entry *new_entry)
2135{
2136 struct mlxsw_sp_fib_node *fib_node;
2137
2138 if (WARN_ON(!fib_entry))
2139 return -EINVAL;
2140
2141 fib_node = fib_entry->fib_node;
2142 list_for_each_entry_from(fib_entry, &fib_node->entry_list, list) {
2143 if (fib_entry->params.tb_id != new_entry->params.tb_id ||
2144 fib_entry->params.tos != new_entry->params.tos ||
2145 fib_entry->params.prio != new_entry->params.prio)
2146 break;
2147 }
2148
2149 list_add_tail(&new_entry->list, &fib_entry->list);
2150 return 0;
2151}
2152
9aecce1c
IS
2153static int
2154mlxsw_sp_fib4_node_list_insert(struct mlxsw_sp_fib_node *fib_node,
4283bce5 2155 struct mlxsw_sp_fib_entry *new_entry,
599cf8f9 2156 bool replace, bool append)
9aecce1c
IS
2157{
2158 struct mlxsw_sp_fib_entry *fib_entry;
2159
2160 fib_entry = mlxsw_sp_fib4_node_entry_find(fib_node, &new_entry->params);
2161
4283bce5
IS
2162 if (append)
2163 return mlxsw_sp_fib4_node_list_append(fib_entry, new_entry);
599cf8f9
IS
2164 if (replace && WARN_ON(!fib_entry))
2165 return -EINVAL;
4283bce5 2166
599cf8f9
IS
2167 /* Insert new entry before replaced one, so that we can later
2168 * remove the second.
2169 */
9aecce1c
IS
2170 if (fib_entry) {
2171 list_add_tail(&new_entry->list, &fib_entry->list);
2172 } else {
2173 struct mlxsw_sp_fib_entry *last;
2174
2175 list_for_each_entry(last, &fib_node->entry_list, list) {
2176 if (new_entry->params.tb_id > last->params.tb_id)
2177 break;
2178 fib_entry = last;
2179 }
2180
2181 if (fib_entry)
2182 list_add(&new_entry->list, &fib_entry->list);
2183 else
2184 list_add(&new_entry->list, &fib_node->entry_list);
2185 }
2186
2187 return 0;
2188}
2189
2190static void
2191mlxsw_sp_fib4_node_list_remove(struct mlxsw_sp_fib_entry *fib_entry)
2192{
2193 list_del(&fib_entry->list);
2194}
2195
2196static int
2197mlxsw_sp_fib4_node_entry_add(struct mlxsw_sp *mlxsw_sp,
2198 const struct mlxsw_sp_fib_node *fib_node,
2199 struct mlxsw_sp_fib_entry *fib_entry)
2200{
2201 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
2202 return 0;
2203
2204 /* To prevent packet loss, overwrite the previously offloaded
2205 * entry.
2206 */
2207 if (!list_is_singular(&fib_node->entry_list)) {
2208 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
2209 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
2210
2211 mlxsw_sp_fib_entry_offload_refresh(n, op, 0);
2212 }
2213
2214 return mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
2215}
2216
2217static void
2218mlxsw_sp_fib4_node_entry_del(struct mlxsw_sp *mlxsw_sp,
2219 const struct mlxsw_sp_fib_node *fib_node,
2220 struct mlxsw_sp_fib_entry *fib_entry)
2221{
2222 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
2223 return;
2224
2225 /* Promote the next entry by overwriting the deleted entry */
2226 if (!list_is_singular(&fib_node->entry_list)) {
2227 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
2228 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
2229
2230 mlxsw_sp_fib_entry_update(mlxsw_sp, n);
2231 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
2232 return;
2233 }
2234
2235 mlxsw_sp_fib_entry_del(mlxsw_sp, fib_entry);
2236}
2237
2238static int mlxsw_sp_fib4_node_entry_link(struct mlxsw_sp *mlxsw_sp,
4283bce5 2239 struct mlxsw_sp_fib_entry *fib_entry,
599cf8f9 2240 bool replace, bool append)
9aecce1c
IS
2241{
2242 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
2243 int err;
2244
599cf8f9
IS
2245 err = mlxsw_sp_fib4_node_list_insert(fib_node, fib_entry, replace,
2246 append);
9aecce1c
IS
2247 if (err)
2248 return err;
2249
2250 err = mlxsw_sp_fib4_node_entry_add(mlxsw_sp, fib_node, fib_entry);
2251 if (err)
2252 goto err_fib4_node_entry_add;
2253
2254 mlxsw_sp_fib_node_prefix_inc(fib_node);
2255
2256 return 0;
2257
2258err_fib4_node_entry_add:
2259 mlxsw_sp_fib4_node_list_remove(fib_entry);
2260 return err;
2261}
2262
2263static void
2264mlxsw_sp_fib4_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
2265 struct mlxsw_sp_fib_entry *fib_entry)
2266{
2267 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
2268
2269 mlxsw_sp_fib_node_prefix_dec(fib_node);
2270 mlxsw_sp_fib4_node_entry_del(mlxsw_sp, fib_node, fib_entry);
2271 mlxsw_sp_fib4_node_list_remove(fib_entry);
2272}
2273
599cf8f9
IS
2274static void mlxsw_sp_fib4_entry_replace(struct mlxsw_sp *mlxsw_sp,
2275 struct mlxsw_sp_fib_entry *fib_entry,
2276 bool replace)
2277{
2278 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
2279 struct mlxsw_sp_fib_entry *replaced;
2280
2281 if (!replace)
2282 return;
2283
2284 /* We inserted the new entry before replaced one */
2285 replaced = list_next_entry(fib_entry, list);
2286
2287 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, replaced);
2288 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, replaced);
2289 mlxsw_sp_fib4_node_put(mlxsw_sp, fib_node);
2290}
2291
9aecce1c
IS
2292static int
2293mlxsw_sp_router_fib4_add(struct mlxsw_sp *mlxsw_sp,
4283bce5 2294 const struct fib_entry_notifier_info *fen_info,
599cf8f9 2295 bool replace, bool append)
9aecce1c
IS
2296{
2297 struct mlxsw_sp_fib_entry *fib_entry;
2298 struct mlxsw_sp_fib_node *fib_node;
61c503f9
JP
2299 int err;
2300
b45f64d1
JP
2301 if (mlxsw_sp->router.aborted)
2302 return 0;
2303
9aecce1c
IS
2304 fib_node = mlxsw_sp_fib4_node_get(mlxsw_sp, fen_info);
2305 if (IS_ERR(fib_node)) {
2306 dev_warn(mlxsw_sp->bus_info->dev, "Failed to get FIB node\n");
2307 return PTR_ERR(fib_node);
b45f64d1 2308 }
61c503f9 2309
9aecce1c
IS
2310 fib_entry = mlxsw_sp_fib4_entry_create(mlxsw_sp, fib_node, fen_info);
2311 if (IS_ERR(fib_entry)) {
2312 dev_warn(mlxsw_sp->bus_info->dev, "Failed to create FIB entry\n");
2313 err = PTR_ERR(fib_entry);
2314 goto err_fib4_entry_create;
2315 }
5b004412 2316
599cf8f9
IS
2317 err = mlxsw_sp_fib4_node_entry_link(mlxsw_sp, fib_entry, replace,
2318 append);
b45f64d1 2319 if (err) {
9aecce1c
IS
2320 dev_warn(mlxsw_sp->bus_info->dev, "Failed to link FIB entry to node\n");
2321 goto err_fib4_node_entry_link;
b45f64d1 2322 }
9aecce1c 2323
599cf8f9
IS
2324 mlxsw_sp_fib4_entry_replace(mlxsw_sp, fib_entry, replace);
2325
61c503f9
JP
2326 return 0;
2327
9aecce1c
IS
2328err_fib4_node_entry_link:
2329 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib_entry);
2330err_fib4_entry_create:
2331 mlxsw_sp_fib4_node_put(mlxsw_sp, fib_node);
61c503f9
JP
2332 return err;
2333}
2334
37956d78
JP
2335static void mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp,
2336 struct fib_entry_notifier_info *fen_info)
61c503f9 2337{
61c503f9 2338 struct mlxsw_sp_fib_entry *fib_entry;
9aecce1c 2339 struct mlxsw_sp_fib_node *fib_node;
61c503f9 2340
b45f64d1 2341 if (mlxsw_sp->router.aborted)
37956d78 2342 return;
b45f64d1 2343
9aecce1c
IS
2344 fib_entry = mlxsw_sp_fib4_entry_lookup(mlxsw_sp, fen_info);
2345 if (WARN_ON(!fib_entry))
37956d78 2346 return;
9aecce1c 2347 fib_node = fib_entry->fib_node;
5b004412 2348
9aecce1c
IS
2349 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib_entry);
2350 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib_entry);
2351 mlxsw_sp_fib4_node_put(mlxsw_sp, fib_node);
61c503f9 2352}
b45f64d1
JP
2353
2354static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp)
2355{
2356 char ralta_pl[MLXSW_REG_RALTA_LEN];
2357 char ralst_pl[MLXSW_REG_RALST_LEN];
2358 char raltb_pl[MLXSW_REG_RALTB_LEN];
2359 char ralue_pl[MLXSW_REG_RALUE_LEN];
2360 int err;
2361
2362 mlxsw_reg_ralta_pack(ralta_pl, true, MLXSW_REG_RALXX_PROTOCOL_IPV4,
2363 MLXSW_SP_LPM_TREE_MIN);
2364 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
2365 if (err)
2366 return err;
2367
2368 mlxsw_reg_ralst_pack(ralst_pl, 0xff, MLXSW_SP_LPM_TREE_MIN);
2369 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
2370 if (err)
2371 return err;
2372
19271c1a
JP
2373 mlxsw_reg_raltb_pack(raltb_pl, 0, MLXSW_REG_RALXX_PROTOCOL_IPV4,
2374 MLXSW_SP_LPM_TREE_MIN);
b45f64d1
JP
2375 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
2376 if (err)
2377 return err;
2378
2379 mlxsw_reg_ralue_pack4(ralue_pl, MLXSW_SP_L3_PROTO_IPV4,
2380 MLXSW_REG_RALUE_OP_WRITE_WRITE, 0, 0, 0);
2381 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
2382 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2383}
2384
9aecce1c
IS
2385static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp,
2386 struct mlxsw_sp_fib_node *fib_node)
2387{
2388 struct mlxsw_sp_fib_entry *fib_entry, *tmp;
2389
2390 list_for_each_entry_safe(fib_entry, tmp, &fib_node->entry_list, list) {
2391 bool do_break = &tmp->list == &fib_node->entry_list;
2392
2393 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib_entry);
2394 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib_entry);
2395 mlxsw_sp_fib4_node_put(mlxsw_sp, fib_node);
2396 /* Break when entry list is empty and node was freed.
2397 * Otherwise, we'll access freed memory in the next
2398 * iteration.
2399 */
2400 if (do_break)
2401 break;
2402 }
2403}
2404
2405static void mlxsw_sp_fib_node_flush(struct mlxsw_sp *mlxsw_sp,
2406 struct mlxsw_sp_fib_node *fib_node)
2407{
2408 switch (fib_node->vr->proto) {
2409 case MLXSW_SP_L3_PROTO_IPV4:
2410 mlxsw_sp_fib4_node_flush(mlxsw_sp, fib_node);
2411 break;
2412 case MLXSW_SP_L3_PROTO_IPV6:
2413 WARN_ON_ONCE(1);
2414 break;
2415 }
2416}
2417
ac571de9 2418static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp)
b45f64d1 2419{
9aecce1c 2420 struct mlxsw_sp_fib_node *fib_node, *tmp;
b45f64d1
JP
2421 struct mlxsw_sp_vr *vr;
2422 int i;
b45f64d1 2423
c1a38311 2424 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
b45f64d1 2425 vr = &mlxsw_sp->router.vrs[i];
ac571de9 2426
b45f64d1
JP
2427 if (!vr->used)
2428 continue;
2429
9aecce1c
IS
2430 list_for_each_entry_safe(fib_node, tmp, &vr->fib->node_list,
2431 list) {
2432 bool do_break = &tmp->list == &vr->fib->node_list;
b45f64d1 2433
9aecce1c 2434 mlxsw_sp_fib_node_flush(mlxsw_sp, fib_node);
b45f64d1
JP
2435 if (do_break)
2436 break;
2437 }
2438 }
ac571de9
IS
2439}
2440
2441static void mlxsw_sp_router_fib4_abort(struct mlxsw_sp *mlxsw_sp)
2442{
2443 int err;
2444
d331d303
IS
2445 if (mlxsw_sp->router.aborted)
2446 return;
2447 dev_warn(mlxsw_sp->bus_info->dev, "FIB abort triggered. Note that FIB entries are no longer being offloaded to this device.\n");
ac571de9 2448 mlxsw_sp_router_fib_flush(mlxsw_sp);
b45f64d1
JP
2449 mlxsw_sp->router.aborted = true;
2450 err = mlxsw_sp_router_set_abort_trap(mlxsw_sp);
2451 if (err)
2452 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set abort trap.\n");
2453}
2454
9665b745
IS
2455static int mlxsw_sp_router_rif_disable(struct mlxsw_sp *mlxsw_sp, u16 rif)
2456{
2457 char ritr_pl[MLXSW_REG_RITR_LEN];
2458 int err;
2459
2460 mlxsw_reg_ritr_rif_pack(ritr_pl, rif);
2461 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
2462 if (WARN_ON_ONCE(err))
2463 return err;
2464
2465 mlxsw_reg_ritr_enable_set(ritr_pl, false);
2466 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
2467}
2468
2469void mlxsw_sp_router_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
2470 struct mlxsw_sp_rif *r)
2471{
2472 mlxsw_sp_router_rif_disable(mlxsw_sp, r->rif);
2473 mlxsw_sp_nexthop_rif_gone_sync(mlxsw_sp, r);
2474 mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, r);
2475}
2476
b45f64d1
JP
2477static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
2478{
b45f64d1 2479 char rgcr_pl[MLXSW_REG_RGCR_LEN];
c1a38311 2480 u64 max_rifs;
b45f64d1
JP
2481 int err;
2482
c1a38311 2483 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_RIFS))
b45f64d1
JP
2484 return -EIO;
2485
c1a38311
JP
2486 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
2487 mlxsw_sp->rifs = kcalloc(max_rifs, sizeof(struct mlxsw_sp_rif *),
2488 GFP_KERNEL);
b45f64d1
JP
2489 if (!mlxsw_sp->rifs)
2490 return -ENOMEM;
2491
2492 mlxsw_reg_rgcr_pack(rgcr_pl, true);
c1a38311 2493 mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, max_rifs);
b45f64d1
JP
2494 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
2495 if (err)
2496 goto err_rgcr_fail;
2497
2498 return 0;
2499
2500err_rgcr_fail:
2501 kfree(mlxsw_sp->rifs);
2502 return err;
2503}
2504
2505static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
2506{
b45f64d1
JP
2507 char rgcr_pl[MLXSW_REG_RGCR_LEN];
2508 int i;
2509
2510 mlxsw_reg_rgcr_pack(rgcr_pl, false);
2511 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
2512
c1a38311 2513 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
b45f64d1
JP
2514 WARN_ON_ONCE(mlxsw_sp->rifs[i]);
2515
2516 kfree(mlxsw_sp->rifs);
2517}
2518
3057224e 2519struct mlxsw_sp_fib_event_work {
a0e4761d 2520 struct work_struct work;
ad178c8e
IS
2521 union {
2522 struct fib_entry_notifier_info fen_info;
2523 struct fib_nh_notifier_info fnh_info;
2524 };
3057224e
IS
2525 struct mlxsw_sp *mlxsw_sp;
2526 unsigned long event;
2527};
2528
2529static void mlxsw_sp_router_fib_event_work(struct work_struct *work)
b45f64d1 2530{
3057224e 2531 struct mlxsw_sp_fib_event_work *fib_work =
a0e4761d 2532 container_of(work, struct mlxsw_sp_fib_event_work, work);
3057224e 2533 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
599cf8f9 2534 bool replace, append;
b45f64d1
JP
2535 int err;
2536
3057224e
IS
2537 /* Protect internal structures from changes */
2538 rtnl_lock();
2539 switch (fib_work->event) {
599cf8f9 2540 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
4283bce5 2541 case FIB_EVENT_ENTRY_APPEND: /* fall through */
b45f64d1 2542 case FIB_EVENT_ENTRY_ADD:
599cf8f9 2543 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
4283bce5
IS
2544 append = fib_work->event == FIB_EVENT_ENTRY_APPEND;
2545 err = mlxsw_sp_router_fib4_add(mlxsw_sp, &fib_work->fen_info,
599cf8f9 2546 replace, append);
b45f64d1
JP
2547 if (err)
2548 mlxsw_sp_router_fib4_abort(mlxsw_sp);
3057224e 2549 fib_info_put(fib_work->fen_info.fi);
b45f64d1
JP
2550 break;
2551 case FIB_EVENT_ENTRY_DEL:
3057224e
IS
2552 mlxsw_sp_router_fib4_del(mlxsw_sp, &fib_work->fen_info);
2553 fib_info_put(fib_work->fen_info.fi);
b45f64d1
JP
2554 break;
2555 case FIB_EVENT_RULE_ADD: /* fall through */
2556 case FIB_EVENT_RULE_DEL:
2557 mlxsw_sp_router_fib4_abort(mlxsw_sp);
2558 break;
ad178c8e
IS
2559 case FIB_EVENT_NH_ADD: /* fall through */
2560 case FIB_EVENT_NH_DEL:
2561 mlxsw_sp_nexthop_event(mlxsw_sp, fib_work->event,
2562 fib_work->fnh_info.fib_nh);
2563 fib_info_put(fib_work->fnh_info.fib_nh->nh_parent);
2564 break;
b45f64d1 2565 }
3057224e
IS
2566 rtnl_unlock();
2567 kfree(fib_work);
2568}
2569
2570/* Called with rcu_read_lock() */
2571static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
2572 unsigned long event, void *ptr)
2573{
2574 struct mlxsw_sp *mlxsw_sp = container_of(nb, struct mlxsw_sp, fib_nb);
2575 struct mlxsw_sp_fib_event_work *fib_work;
2576 struct fib_notifier_info *info = ptr;
2577
2578 if (!net_eq(info->net, &init_net))
2579 return NOTIFY_DONE;
2580
2581 fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC);
2582 if (WARN_ON(!fib_work))
2583 return NOTIFY_BAD;
2584
a0e4761d 2585 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib_event_work);
3057224e
IS
2586 fib_work->mlxsw_sp = mlxsw_sp;
2587 fib_work->event = event;
2588
2589 switch (event) {
599cf8f9 2590 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
4283bce5 2591 case FIB_EVENT_ENTRY_APPEND: /* fall through */
3057224e
IS
2592 case FIB_EVENT_ENTRY_ADD: /* fall through */
2593 case FIB_EVENT_ENTRY_DEL:
2594 memcpy(&fib_work->fen_info, ptr, sizeof(fib_work->fen_info));
2595 /* Take referece on fib_info to prevent it from being
2596 * freed while work is queued. Release it afterwards.
2597 */
2598 fib_info_hold(fib_work->fen_info.fi);
2599 break;
ad178c8e
IS
2600 case FIB_EVENT_NH_ADD: /* fall through */
2601 case FIB_EVENT_NH_DEL:
2602 memcpy(&fib_work->fnh_info, ptr, sizeof(fib_work->fnh_info));
2603 fib_info_hold(fib_work->fnh_info.fib_nh->nh_parent);
2604 break;
3057224e
IS
2605 }
2606
a0e4761d 2607 mlxsw_core_schedule_work(&fib_work->work);
3057224e 2608
b45f64d1
JP
2609 return NOTIFY_DONE;
2610}
2611
c3852ef7
IS
2612static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb)
2613{
2614 struct mlxsw_sp *mlxsw_sp = container_of(nb, struct mlxsw_sp, fib_nb);
2615
2616 /* Flush pending FIB notifications and then flush the device's
2617 * table before requesting another dump. The FIB notification
2618 * block is unregistered, so no need to take RTNL.
2619 */
2620 mlxsw_core_flush_owq();
2621 mlxsw_sp_router_fib_flush(mlxsw_sp);
2622}
2623
b45f64d1
JP
2624int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
2625{
2626 int err;
2627
2628 INIT_LIST_HEAD(&mlxsw_sp->router.nexthop_neighs_list);
b45f64d1
JP
2629 err = __mlxsw_sp_router_init(mlxsw_sp);
2630 if (err)
2631 return err;
2632
c53b8e1b
IS
2633 err = rhashtable_init(&mlxsw_sp->router.nexthop_ht,
2634 &mlxsw_sp_nexthop_ht_params);
2635 if (err)
2636 goto err_nexthop_ht_init;
2637
e9ad5e7d
IS
2638 err = rhashtable_init(&mlxsw_sp->router.nexthop_group_ht,
2639 &mlxsw_sp_nexthop_group_ht_params);
2640 if (err)
2641 goto err_nexthop_group_ht_init;
2642
b45f64d1
JP
2643 mlxsw_sp_lpm_init(mlxsw_sp);
2644 err = mlxsw_sp_vrs_init(mlxsw_sp);
2645 if (err)
2646 goto err_vrs_init;
2647
8c9583a8 2648 err = mlxsw_sp_neigh_init(mlxsw_sp);
b45f64d1
JP
2649 if (err)
2650 goto err_neigh_init;
2651
2652 mlxsw_sp->fib_nb.notifier_call = mlxsw_sp_router_fib_event;
c3852ef7
IS
2653 err = register_fib_notifier(&mlxsw_sp->fib_nb,
2654 mlxsw_sp_router_fib_dump_flush);
2655 if (err)
2656 goto err_register_fib_notifier;
2657
b45f64d1
JP
2658 return 0;
2659
c3852ef7
IS
2660err_register_fib_notifier:
2661 mlxsw_sp_neigh_fini(mlxsw_sp);
b45f64d1
JP
2662err_neigh_init:
2663 mlxsw_sp_vrs_fini(mlxsw_sp);
2664err_vrs_init:
e9ad5e7d
IS
2665 rhashtable_destroy(&mlxsw_sp->router.nexthop_group_ht);
2666err_nexthop_group_ht_init:
c53b8e1b
IS
2667 rhashtable_destroy(&mlxsw_sp->router.nexthop_ht);
2668err_nexthop_ht_init:
b45f64d1
JP
2669 __mlxsw_sp_router_fini(mlxsw_sp);
2670 return err;
2671}
2672
2673void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
2674{
2675 unregister_fib_notifier(&mlxsw_sp->fib_nb);
2676 mlxsw_sp_neigh_fini(mlxsw_sp);
2677 mlxsw_sp_vrs_fini(mlxsw_sp);
e9ad5e7d 2678 rhashtable_destroy(&mlxsw_sp->router.nexthop_group_ht);
c53b8e1b 2679 rhashtable_destroy(&mlxsw_sp->router.nexthop_ht);
b45f64d1
JP
2680 __mlxsw_sp_router_fini(mlxsw_sp);
2681}