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