mlxsw: spectrum_router: Don't check state when refreshing offload indication
[linux-2.6-block.git] / drivers / net / ethernet / mellanox / mlxsw / spectrum_router.c
CommitLineData
464dce18
IS
1/*
2 * drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
3 * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
4 * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
5 * Copyright (c) 2016 Ido Schimmel <idosch@mellanox.com>
c723c735 6 * Copyright (c) 2016 Yotam Gigi <yotamg@mellanox.com>
464dce18
IS
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the names of the copyright holders nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * Alternatively, this software may be distributed under the terms of the
21 * GNU General Public License ("GPL") version 2 as published by the Free
22 * Software Foundation.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
35 */
36
37#include <linux/kernel.h>
38#include <linux/types.h>
5e9c16cc
JP
39#include <linux/rhashtable.h>
40#include <linux/bitops.h>
41#include <linux/in6.h>
c723c735 42#include <linux/notifier.h>
df6dd79b 43#include <linux/inetdevice.h>
9db032bb 44#include <linux/netdevice.h>
03ea01e9 45#include <linux/if_bridge.h>
b5f3e0d4 46#include <linux/socket.h>
c723c735 47#include <net/netevent.h>
6cf3c971
JP
48#include <net/neighbour.h>
49#include <net/arp.h>
b45f64d1 50#include <net/ip_fib.h>
5d7bfd14 51#include <net/fib_rules.h>
57837885 52#include <net/l3mdev.h>
5ea1237f 53#include <net/addrconf.h>
d5eb89cf
AS
54#include <net/ndisc.h>
55#include <net/ipv6.h>
464dce18
IS
56
57#include "spectrum.h"
58#include "core.h"
59#include "reg.h"
e0c0afd8
AS
60#include "spectrum_cnt.h"
61#include "spectrum_dpipe.h"
62#include "spectrum_router.h"
464dce18 63
9011b677
IS
64struct mlxsw_sp_vr;
65struct mlxsw_sp_lpm_tree;
e4f3c1c1 66struct mlxsw_sp_rif_ops;
9011b677
IS
67
68struct mlxsw_sp_router {
69 struct mlxsw_sp *mlxsw_sp;
5f9efffb 70 struct mlxsw_sp_rif **rifs;
9011b677
IS
71 struct mlxsw_sp_vr *vrs;
72 struct rhashtable neigh_ht;
73 struct rhashtable nexthop_group_ht;
74 struct rhashtable nexthop_ht;
75 struct {
76 struct mlxsw_sp_lpm_tree *trees;
77 unsigned int tree_count;
78 } lpm;
79 struct {
80 struct delayed_work dw;
81 unsigned long interval; /* ms */
82 } neighs_update;
83 struct delayed_work nexthop_probe_dw;
84#define MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL 5000 /* ms */
85 struct list_head nexthop_neighs_list;
86 bool aborted;
7e39d115 87 struct notifier_block fib_nb;
e4f3c1c1 88 const struct mlxsw_sp_rif_ops **rif_ops_arr;
9011b677
IS
89};
90
4724ba56
IS
91struct mlxsw_sp_rif {
92 struct list_head nexthop_list;
93 struct list_head neigh_list;
94 struct net_device *dev;
a1107487 95 struct mlxsw_sp_fid *fid;
4724ba56
IS
96 unsigned char addr[ETH_ALEN];
97 int mtu;
bf95233e 98 u16 rif_index;
6913229e 99 u16 vr_id;
e4f3c1c1
IS
100 const struct mlxsw_sp_rif_ops *ops;
101 struct mlxsw_sp *mlxsw_sp;
102
e0c0afd8
AS
103 unsigned int counter_ingress;
104 bool counter_ingress_valid;
105 unsigned int counter_egress;
106 bool counter_egress_valid;
4724ba56
IS
107};
108
e4f3c1c1
IS
109struct mlxsw_sp_rif_params {
110 struct net_device *dev;
111 union {
112 u16 system_port;
113 u16 lag_id;
114 };
115 u16 vid;
116 bool lag;
117};
118
4d93ceeb
IS
119struct mlxsw_sp_rif_subport {
120 struct mlxsw_sp_rif common;
121 union {
122 u16 system_port;
123 u16 lag_id;
124 };
125 u16 vid;
126 bool lag;
127};
128
e4f3c1c1
IS
129struct mlxsw_sp_rif_ops {
130 enum mlxsw_sp_rif_type type;
131 size_t rif_size;
132
133 void (*setup)(struct mlxsw_sp_rif *rif,
134 const struct mlxsw_sp_rif_params *params);
135 int (*configure)(struct mlxsw_sp_rif *rif);
136 void (*deconfigure)(struct mlxsw_sp_rif *rif);
137 struct mlxsw_sp_fid * (*fid_get)(struct mlxsw_sp_rif *rif);
138};
139
e0c0afd8
AS
140static unsigned int *
141mlxsw_sp_rif_p_counter_get(struct mlxsw_sp_rif *rif,
142 enum mlxsw_sp_rif_counter_dir dir)
143{
144 switch (dir) {
145 case MLXSW_SP_RIF_COUNTER_EGRESS:
146 return &rif->counter_egress;
147 case MLXSW_SP_RIF_COUNTER_INGRESS:
148 return &rif->counter_ingress;
149 }
150 return NULL;
151}
152
153static bool
154mlxsw_sp_rif_counter_valid_get(struct mlxsw_sp_rif *rif,
155 enum mlxsw_sp_rif_counter_dir dir)
156{
157 switch (dir) {
158 case MLXSW_SP_RIF_COUNTER_EGRESS:
159 return rif->counter_egress_valid;
160 case MLXSW_SP_RIF_COUNTER_INGRESS:
161 return rif->counter_ingress_valid;
162 }
163 return false;
164}
165
166static void
167mlxsw_sp_rif_counter_valid_set(struct mlxsw_sp_rif *rif,
168 enum mlxsw_sp_rif_counter_dir dir,
169 bool valid)
170{
171 switch (dir) {
172 case MLXSW_SP_RIF_COUNTER_EGRESS:
173 rif->counter_egress_valid = valid;
174 break;
175 case MLXSW_SP_RIF_COUNTER_INGRESS:
176 rif->counter_ingress_valid = valid;
177 break;
178 }
179}
180
181static int mlxsw_sp_rif_counter_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
182 unsigned int counter_index, bool enable,
183 enum mlxsw_sp_rif_counter_dir dir)
184{
185 char ritr_pl[MLXSW_REG_RITR_LEN];
186 bool is_egress = false;
187 int err;
188
189 if (dir == MLXSW_SP_RIF_COUNTER_EGRESS)
190 is_egress = true;
191 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
192 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
193 if (err)
194 return err;
195
196 mlxsw_reg_ritr_counter_pack(ritr_pl, counter_index, enable,
197 is_egress);
198 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
199}
200
201int mlxsw_sp_rif_counter_value_get(struct mlxsw_sp *mlxsw_sp,
202 struct mlxsw_sp_rif *rif,
203 enum mlxsw_sp_rif_counter_dir dir, u64 *cnt)
204{
205 char ricnt_pl[MLXSW_REG_RICNT_LEN];
206 unsigned int *p_counter_index;
207 bool valid;
208 int err;
209
210 valid = mlxsw_sp_rif_counter_valid_get(rif, dir);
211 if (!valid)
212 return -EINVAL;
213
214 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
215 if (!p_counter_index)
216 return -EINVAL;
217 mlxsw_reg_ricnt_pack(ricnt_pl, *p_counter_index,
218 MLXSW_REG_RICNT_OPCODE_NOP);
219 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
220 if (err)
221 return err;
222 *cnt = mlxsw_reg_ricnt_good_unicast_packets_get(ricnt_pl);
223 return 0;
224}
225
226static int mlxsw_sp_rif_counter_clear(struct mlxsw_sp *mlxsw_sp,
227 unsigned int counter_index)
228{
229 char ricnt_pl[MLXSW_REG_RICNT_LEN];
230
231 mlxsw_reg_ricnt_pack(ricnt_pl, counter_index,
232 MLXSW_REG_RICNT_OPCODE_CLEAR);
233 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
234}
235
236int mlxsw_sp_rif_counter_alloc(struct mlxsw_sp *mlxsw_sp,
237 struct mlxsw_sp_rif *rif,
238 enum mlxsw_sp_rif_counter_dir dir)
239{
240 unsigned int *p_counter_index;
241 int err;
242
243 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
244 if (!p_counter_index)
245 return -EINVAL;
246 err = mlxsw_sp_counter_alloc(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
247 p_counter_index);
248 if (err)
249 return err;
250
251 err = mlxsw_sp_rif_counter_clear(mlxsw_sp, *p_counter_index);
252 if (err)
253 goto err_counter_clear;
254
255 err = mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
256 *p_counter_index, true, dir);
257 if (err)
258 goto err_counter_edit;
259 mlxsw_sp_rif_counter_valid_set(rif, dir, true);
260 return 0;
261
262err_counter_edit:
263err_counter_clear:
264 mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
265 *p_counter_index);
266 return err;
267}
268
269void mlxsw_sp_rif_counter_free(struct mlxsw_sp *mlxsw_sp,
270 struct mlxsw_sp_rif *rif,
271 enum mlxsw_sp_rif_counter_dir dir)
272{
273 unsigned int *p_counter_index;
274
6b1206bb
AS
275 if (!mlxsw_sp_rif_counter_valid_get(rif, dir))
276 return;
277
e0c0afd8
AS
278 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
279 if (WARN_ON(!p_counter_index))
280 return;
281 mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
282 *p_counter_index, false, dir);
283 mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
284 *p_counter_index);
285 mlxsw_sp_rif_counter_valid_set(rif, dir, false);
286}
287
e4f3c1c1
IS
288static void mlxsw_sp_rif_counters_alloc(struct mlxsw_sp_rif *rif)
289{
290 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
291 struct devlink *devlink;
292
293 devlink = priv_to_devlink(mlxsw_sp->core);
294 if (!devlink_dpipe_table_counter_enabled(devlink,
295 MLXSW_SP_DPIPE_TABLE_NAME_ERIF))
296 return;
297 mlxsw_sp_rif_counter_alloc(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS);
298}
299
300static void mlxsw_sp_rif_counters_free(struct mlxsw_sp_rif *rif)
301{
302 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
303
304 mlxsw_sp_rif_counter_free(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS);
305}
306
4724ba56
IS
307static struct mlxsw_sp_rif *
308mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
309 const struct net_device *dev);
310
7dcc18ad 311#define MLXSW_SP_PREFIX_COUNT (sizeof(struct in6_addr) * BITS_PER_BYTE + 1)
9011b677
IS
312
313struct mlxsw_sp_prefix_usage {
314 DECLARE_BITMAP(b, MLXSW_SP_PREFIX_COUNT);
315};
316
53342023
JP
317#define mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) \
318 for_each_set_bit(prefix, (prefix_usage)->b, MLXSW_SP_PREFIX_COUNT)
319
6b75c480
JP
320static bool
321mlxsw_sp_prefix_usage_subset(struct mlxsw_sp_prefix_usage *prefix_usage1,
322 struct mlxsw_sp_prefix_usage *prefix_usage2)
323{
324 unsigned char prefix;
325
326 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage1) {
327 if (!test_bit(prefix, prefix_usage2->b))
328 return false;
329 }
330 return true;
331}
332
53342023
JP
333static bool
334mlxsw_sp_prefix_usage_eq(struct mlxsw_sp_prefix_usage *prefix_usage1,
335 struct mlxsw_sp_prefix_usage *prefix_usage2)
336{
337 return !memcmp(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
338}
339
6b75c480
JP
340static bool
341mlxsw_sp_prefix_usage_none(struct mlxsw_sp_prefix_usage *prefix_usage)
342{
343 struct mlxsw_sp_prefix_usage prefix_usage_none = {{ 0 } };
344
345 return mlxsw_sp_prefix_usage_eq(prefix_usage, &prefix_usage_none);
346}
347
348static void
349mlxsw_sp_prefix_usage_cpy(struct mlxsw_sp_prefix_usage *prefix_usage1,
350 struct mlxsw_sp_prefix_usage *prefix_usage2)
351{
352 memcpy(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
353}
354
5e9c16cc
JP
355static void
356mlxsw_sp_prefix_usage_set(struct mlxsw_sp_prefix_usage *prefix_usage,
357 unsigned char prefix_len)
358{
359 set_bit(prefix_len, prefix_usage->b);
360}
361
362static void
363mlxsw_sp_prefix_usage_clear(struct mlxsw_sp_prefix_usage *prefix_usage,
364 unsigned char prefix_len)
365{
366 clear_bit(prefix_len, prefix_usage->b);
367}
368
369struct mlxsw_sp_fib_key {
370 unsigned char addr[sizeof(struct in6_addr)];
371 unsigned char prefix_len;
372};
373
61c503f9
JP
374enum mlxsw_sp_fib_entry_type {
375 MLXSW_SP_FIB_ENTRY_TYPE_REMOTE,
376 MLXSW_SP_FIB_ENTRY_TYPE_LOCAL,
377 MLXSW_SP_FIB_ENTRY_TYPE_TRAP,
378};
379
a7ff87ac 380struct mlxsw_sp_nexthop_group;
9011b677 381struct mlxsw_sp_fib;
a7ff87ac 382
9aecce1c
IS
383struct mlxsw_sp_fib_node {
384 struct list_head entry_list;
b45f64d1 385 struct list_head list;
9aecce1c 386 struct rhash_head ht_node;
76610ebb 387 struct mlxsw_sp_fib *fib;
5e9c16cc 388 struct mlxsw_sp_fib_key key;
9aecce1c
IS
389};
390
9aecce1c
IS
391struct mlxsw_sp_fib_entry {
392 struct list_head list;
393 struct mlxsw_sp_fib_node *fib_node;
61c503f9 394 enum mlxsw_sp_fib_entry_type type;
a7ff87ac
JP
395 struct list_head nexthop_group_node;
396 struct mlxsw_sp_nexthop_group *nh_group;
5e9c16cc
JP
397};
398
4f1c7f1f
IS
399struct mlxsw_sp_fib4_entry {
400 struct mlxsw_sp_fib_entry common;
401 u32 tb_id;
402 u32 prio;
403 u8 tos;
404 u8 type;
405};
406
9011b677
IS
407enum mlxsw_sp_l3proto {
408 MLXSW_SP_L3_PROTO_IPV4,
409 MLXSW_SP_L3_PROTO_IPV6,
410};
411
412struct mlxsw_sp_lpm_tree {
413 u8 id; /* tree ID */
414 unsigned int ref_count;
415 enum mlxsw_sp_l3proto proto;
416 struct mlxsw_sp_prefix_usage prefix_usage;
417};
418
5e9c16cc
JP
419struct mlxsw_sp_fib {
420 struct rhashtable ht;
9aecce1c 421 struct list_head node_list;
76610ebb
IS
422 struct mlxsw_sp_vr *vr;
423 struct mlxsw_sp_lpm_tree *lpm_tree;
5e9c16cc
JP
424 unsigned long prefix_ref_count[MLXSW_SP_PREFIX_COUNT];
425 struct mlxsw_sp_prefix_usage prefix_usage;
76610ebb 426 enum mlxsw_sp_l3proto proto;
5e9c16cc
JP
427};
428
9011b677
IS
429struct mlxsw_sp_vr {
430 u16 id; /* virtual router ID */
431 u32 tb_id; /* kernel fib table id */
432 unsigned int rif_count;
433 struct mlxsw_sp_fib *fib4;
a3d9bc50 434 struct mlxsw_sp_fib *fib6;
9011b677
IS
435};
436
9aecce1c 437static const struct rhashtable_params mlxsw_sp_fib_ht_params;
5e9c16cc 438
76610ebb
IS
439static struct mlxsw_sp_fib *mlxsw_sp_fib_create(struct mlxsw_sp_vr *vr,
440 enum mlxsw_sp_l3proto proto)
5e9c16cc
JP
441{
442 struct mlxsw_sp_fib *fib;
443 int err;
444
445 fib = kzalloc(sizeof(*fib), GFP_KERNEL);
446 if (!fib)
447 return ERR_PTR(-ENOMEM);
448 err = rhashtable_init(&fib->ht, &mlxsw_sp_fib_ht_params);
449 if (err)
450 goto err_rhashtable_init;
9aecce1c 451 INIT_LIST_HEAD(&fib->node_list);
76610ebb
IS
452 fib->proto = proto;
453 fib->vr = vr;
5e9c16cc
JP
454 return fib;
455
456err_rhashtable_init:
457 kfree(fib);
458 return ERR_PTR(err);
459}
460
461static void mlxsw_sp_fib_destroy(struct mlxsw_sp_fib *fib)
462{
9aecce1c 463 WARN_ON(!list_empty(&fib->node_list));
76610ebb 464 WARN_ON(fib->lpm_tree);
5e9c16cc
JP
465 rhashtable_destroy(&fib->ht);
466 kfree(fib);
467}
468
53342023 469static struct mlxsw_sp_lpm_tree *
382dbb40 470mlxsw_sp_lpm_tree_find_unused(struct mlxsw_sp *mlxsw_sp)
53342023
JP
471{
472 static struct mlxsw_sp_lpm_tree *lpm_tree;
473 int i;
474
9011b677
IS
475 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
476 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
382dbb40
IS
477 if (lpm_tree->ref_count == 0)
478 return lpm_tree;
53342023
JP
479 }
480 return NULL;
481}
482
483static int mlxsw_sp_lpm_tree_alloc(struct mlxsw_sp *mlxsw_sp,
484 struct mlxsw_sp_lpm_tree *lpm_tree)
485{
486 char ralta_pl[MLXSW_REG_RALTA_LEN];
487
1a9234e6
IS
488 mlxsw_reg_ralta_pack(ralta_pl, true,
489 (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
490 lpm_tree->id);
53342023
JP
491 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
492}
493
494static int mlxsw_sp_lpm_tree_free(struct mlxsw_sp *mlxsw_sp,
495 struct mlxsw_sp_lpm_tree *lpm_tree)
496{
497 char ralta_pl[MLXSW_REG_RALTA_LEN];
498
1a9234e6
IS
499 mlxsw_reg_ralta_pack(ralta_pl, false,
500 (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
501 lpm_tree->id);
53342023
JP
502 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
503}
504
505static int
506mlxsw_sp_lpm_tree_left_struct_set(struct mlxsw_sp *mlxsw_sp,
507 struct mlxsw_sp_prefix_usage *prefix_usage,
508 struct mlxsw_sp_lpm_tree *lpm_tree)
509{
510 char ralst_pl[MLXSW_REG_RALST_LEN];
511 u8 root_bin = 0;
512 u8 prefix;
513 u8 last_prefix = MLXSW_REG_RALST_BIN_NO_CHILD;
514
515 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage)
516 root_bin = prefix;
517
518 mlxsw_reg_ralst_pack(ralst_pl, root_bin, lpm_tree->id);
519 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) {
520 if (prefix == 0)
521 continue;
522 mlxsw_reg_ralst_bin_pack(ralst_pl, prefix, last_prefix,
523 MLXSW_REG_RALST_BIN_NO_CHILD);
524 last_prefix = prefix;
525 }
526 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
527}
528
529static struct mlxsw_sp_lpm_tree *
530mlxsw_sp_lpm_tree_create(struct mlxsw_sp *mlxsw_sp,
531 struct mlxsw_sp_prefix_usage *prefix_usage,
382dbb40 532 enum mlxsw_sp_l3proto proto)
53342023
JP
533{
534 struct mlxsw_sp_lpm_tree *lpm_tree;
535 int err;
536
382dbb40 537 lpm_tree = mlxsw_sp_lpm_tree_find_unused(mlxsw_sp);
53342023
JP
538 if (!lpm_tree)
539 return ERR_PTR(-EBUSY);
540 lpm_tree->proto = proto;
541 err = mlxsw_sp_lpm_tree_alloc(mlxsw_sp, lpm_tree);
542 if (err)
543 return ERR_PTR(err);
544
545 err = mlxsw_sp_lpm_tree_left_struct_set(mlxsw_sp, prefix_usage,
546 lpm_tree);
547 if (err)
548 goto err_left_struct_set;
2083d367
JP
549 memcpy(&lpm_tree->prefix_usage, prefix_usage,
550 sizeof(lpm_tree->prefix_usage));
53342023
JP
551 return lpm_tree;
552
553err_left_struct_set:
554 mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
555 return ERR_PTR(err);
556}
557
558static int mlxsw_sp_lpm_tree_destroy(struct mlxsw_sp *mlxsw_sp,
559 struct mlxsw_sp_lpm_tree *lpm_tree)
560{
561 return mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
562}
563
564static struct mlxsw_sp_lpm_tree *
565mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp,
566 struct mlxsw_sp_prefix_usage *prefix_usage,
382dbb40 567 enum mlxsw_sp_l3proto proto)
53342023
JP
568{
569 struct mlxsw_sp_lpm_tree *lpm_tree;
570 int i;
571
9011b677
IS
572 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
573 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
8b99becd
JP
574 if (lpm_tree->ref_count != 0 &&
575 lpm_tree->proto == proto &&
53342023
JP
576 mlxsw_sp_prefix_usage_eq(&lpm_tree->prefix_usage,
577 prefix_usage))
578 goto inc_ref_count;
579 }
580 lpm_tree = mlxsw_sp_lpm_tree_create(mlxsw_sp, prefix_usage,
382dbb40 581 proto);
53342023
JP
582 if (IS_ERR(lpm_tree))
583 return lpm_tree;
584
585inc_ref_count:
586 lpm_tree->ref_count++;
587 return lpm_tree;
588}
589
590static int mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp,
591 struct mlxsw_sp_lpm_tree *lpm_tree)
592{
593 if (--lpm_tree->ref_count == 0)
594 return mlxsw_sp_lpm_tree_destroy(mlxsw_sp, lpm_tree);
595 return 0;
596}
597
d7a60306 598#define MLXSW_SP_LPM_TREE_MIN 1 /* tree 0 is reserved */
8494ab06
IS
599
600static int mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp)
53342023
JP
601{
602 struct mlxsw_sp_lpm_tree *lpm_tree;
8494ab06 603 u64 max_trees;
53342023
JP
604 int i;
605
8494ab06
IS
606 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_LPM_TREES))
607 return -EIO;
608
609 max_trees = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_LPM_TREES);
9011b677
IS
610 mlxsw_sp->router->lpm.tree_count = max_trees - MLXSW_SP_LPM_TREE_MIN;
611 mlxsw_sp->router->lpm.trees = kcalloc(mlxsw_sp->router->lpm.tree_count,
8494ab06
IS
612 sizeof(struct mlxsw_sp_lpm_tree),
613 GFP_KERNEL);
9011b677 614 if (!mlxsw_sp->router->lpm.trees)
8494ab06
IS
615 return -ENOMEM;
616
9011b677
IS
617 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
618 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
53342023
JP
619 lpm_tree->id = i + MLXSW_SP_LPM_TREE_MIN;
620 }
8494ab06
IS
621
622 return 0;
623}
624
625static void mlxsw_sp_lpm_fini(struct mlxsw_sp *mlxsw_sp)
626{
9011b677 627 kfree(mlxsw_sp->router->lpm.trees);
53342023
JP
628}
629
76610ebb
IS
630static bool mlxsw_sp_vr_is_used(const struct mlxsw_sp_vr *vr)
631{
a3d9bc50 632 return !!vr->fib4 || !!vr->fib6;
76610ebb
IS
633}
634
6b75c480
JP
635static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp)
636{
637 struct mlxsw_sp_vr *vr;
638 int i;
639
c1a38311 640 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
9011b677 641 vr = &mlxsw_sp->router->vrs[i];
76610ebb 642 if (!mlxsw_sp_vr_is_used(vr))
6b75c480
JP
643 return vr;
644 }
645 return NULL;
646}
647
648static int mlxsw_sp_vr_lpm_tree_bind(struct mlxsw_sp *mlxsw_sp,
76610ebb 649 const struct mlxsw_sp_fib *fib)
6b75c480
JP
650{
651 char raltb_pl[MLXSW_REG_RALTB_LEN];
652
76610ebb
IS
653 mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,
654 (enum mlxsw_reg_ralxx_protocol) fib->proto,
655 fib->lpm_tree->id);
6b75c480
JP
656 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
657}
658
659static int mlxsw_sp_vr_lpm_tree_unbind(struct mlxsw_sp *mlxsw_sp,
76610ebb 660 const struct mlxsw_sp_fib *fib)
6b75c480
JP
661{
662 char raltb_pl[MLXSW_REG_RALTB_LEN];
663
664 /* Bind to tree 0 which is default */
76610ebb
IS
665 mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,
666 (enum mlxsw_reg_ralxx_protocol) fib->proto, 0);
6b75c480
JP
667 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
668}
669
670static u32 mlxsw_sp_fix_tb_id(u32 tb_id)
671{
672 /* For our purpose, squash main and local table into one */
673 if (tb_id == RT_TABLE_LOCAL)
674 tb_id = RT_TABLE_MAIN;
675 return tb_id;
676}
677
678static struct mlxsw_sp_vr *mlxsw_sp_vr_find(struct mlxsw_sp *mlxsw_sp,
76610ebb 679 u32 tb_id)
6b75c480
JP
680{
681 struct mlxsw_sp_vr *vr;
682 int i;
683
684 tb_id = mlxsw_sp_fix_tb_id(tb_id);
9497c042 685
c1a38311 686 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
9011b677 687 vr = &mlxsw_sp->router->vrs[i];
76610ebb 688 if (mlxsw_sp_vr_is_used(vr) && vr->tb_id == tb_id)
6b75c480
JP
689 return vr;
690 }
691 return NULL;
692}
693
76610ebb
IS
694static struct mlxsw_sp_fib *mlxsw_sp_vr_fib(const struct mlxsw_sp_vr *vr,
695 enum mlxsw_sp_l3proto proto)
696{
697 switch (proto) {
698 case MLXSW_SP_L3_PROTO_IPV4:
699 return vr->fib4;
700 case MLXSW_SP_L3_PROTO_IPV6:
a3d9bc50 701 return vr->fib6;
76610ebb
IS
702 }
703 return NULL;
704}
705
6b75c480 706static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp,
76610ebb 707 u32 tb_id)
6b75c480 708{
6b75c480 709 struct mlxsw_sp_vr *vr;
a3d9bc50 710 int err;
6b75c480
JP
711
712 vr = mlxsw_sp_vr_find_unused(mlxsw_sp);
713 if (!vr)
714 return ERR_PTR(-EBUSY);
76610ebb
IS
715 vr->fib4 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV4);
716 if (IS_ERR(vr->fib4))
717 return ERR_CAST(vr->fib4);
a3d9bc50
IS
718 vr->fib6 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV6);
719 if (IS_ERR(vr->fib6)) {
720 err = PTR_ERR(vr->fib6);
721 goto err_fib6_create;
722 }
6b75c480 723 vr->tb_id = tb_id;
6b75c480 724 return vr;
a3d9bc50
IS
725
726err_fib6_create:
727 mlxsw_sp_fib_destroy(vr->fib4);
728 vr->fib4 = NULL;
729 return ERR_PTR(err);
6b75c480
JP
730}
731
76610ebb 732static void mlxsw_sp_vr_destroy(struct mlxsw_sp_vr *vr)
6b75c480 733{
a3d9bc50
IS
734 mlxsw_sp_fib_destroy(vr->fib6);
735 vr->fib6 = NULL;
76610ebb
IS
736 mlxsw_sp_fib_destroy(vr->fib4);
737 vr->fib4 = NULL;
6b75c480
JP
738}
739
740static int
76610ebb 741mlxsw_sp_vr_lpm_tree_check(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib *fib,
6b75c480
JP
742 struct mlxsw_sp_prefix_usage *req_prefix_usage)
743{
76610ebb 744 struct mlxsw_sp_lpm_tree *lpm_tree = fib->lpm_tree;
f7df4923
IS
745 struct mlxsw_sp_lpm_tree *new_tree;
746 int err;
6b75c480 747
f7df4923 748 if (mlxsw_sp_prefix_usage_eq(req_prefix_usage, &lpm_tree->prefix_usage))
6b75c480
JP
749 return 0;
750
f7df4923 751 new_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, req_prefix_usage,
76610ebb 752 fib->proto);
f7df4923 753 if (IS_ERR(new_tree)) {
6b75c480
JP
754 /* We failed to get a tree according to the required
755 * prefix usage. However, the current tree might be still good
756 * for us if our requirement is subset of the prefixes used
757 * in the tree.
758 */
759 if (mlxsw_sp_prefix_usage_subset(req_prefix_usage,
f7df4923 760 &lpm_tree->prefix_usage))
6b75c480 761 return 0;
f7df4923 762 return PTR_ERR(new_tree);
6b75c480
JP
763 }
764
f7df4923 765 /* Prevent packet loss by overwriting existing binding */
76610ebb
IS
766 fib->lpm_tree = new_tree;
767 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib);
f7df4923
IS
768 if (err)
769 goto err_tree_bind;
770 mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree);
771
772 return 0;
773
774err_tree_bind:
76610ebb 775 fib->lpm_tree = lpm_tree;
f7df4923
IS
776 mlxsw_sp_lpm_tree_put(mlxsw_sp, new_tree);
777 return err;
6b75c480
JP
778}
779
76610ebb 780static struct mlxsw_sp_vr *mlxsw_sp_vr_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id)
6b75c480
JP
781{
782 struct mlxsw_sp_vr *vr;
6b75c480
JP
783
784 tb_id = mlxsw_sp_fix_tb_id(tb_id);
76610ebb
IS
785 vr = mlxsw_sp_vr_find(mlxsw_sp, tb_id);
786 if (!vr)
787 vr = mlxsw_sp_vr_create(mlxsw_sp, tb_id);
6b75c480
JP
788 return vr;
789}
790
76610ebb 791static void mlxsw_sp_vr_put(struct mlxsw_sp_vr *vr)
6b75c480 792{
a3d9bc50
IS
793 if (!vr->rif_count && list_empty(&vr->fib4->node_list) &&
794 list_empty(&vr->fib6->node_list))
76610ebb 795 mlxsw_sp_vr_destroy(vr);
6b75c480
JP
796}
797
9497c042 798static int mlxsw_sp_vrs_init(struct mlxsw_sp *mlxsw_sp)
6b75c480
JP
799{
800 struct mlxsw_sp_vr *vr;
c1a38311 801 u64 max_vrs;
6b75c480
JP
802 int i;
803
c1a38311 804 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_VRS))
9497c042
NF
805 return -EIO;
806
c1a38311 807 max_vrs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS);
9011b677
IS
808 mlxsw_sp->router->vrs = kcalloc(max_vrs, sizeof(struct mlxsw_sp_vr),
809 GFP_KERNEL);
810 if (!mlxsw_sp->router->vrs)
9497c042
NF
811 return -ENOMEM;
812
c1a38311 813 for (i = 0; i < max_vrs; i++) {
9011b677 814 vr = &mlxsw_sp->router->vrs[i];
6b75c480
JP
815 vr->id = i;
816 }
9497c042
NF
817
818 return 0;
819}
820
ac571de9
IS
821static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp);
822
9497c042
NF
823static void mlxsw_sp_vrs_fini(struct mlxsw_sp *mlxsw_sp)
824{
3057224e
IS
825 /* At this stage we're guaranteed not to have new incoming
826 * FIB notifications and the work queue is free from FIBs
827 * sitting on top of mlxsw netdevs. However, we can still
828 * have other FIBs queued. Flush the queue before flushing
829 * the device's tables. No need for locks, as we're the only
830 * writer.
831 */
832 mlxsw_core_flush_owq();
ac571de9 833 mlxsw_sp_router_fib_flush(mlxsw_sp);
9011b677 834 kfree(mlxsw_sp->router->vrs);
6b75c480
JP
835}
836
6cf3c971 837struct mlxsw_sp_neigh_key {
33b1341c 838 struct neighbour *n;
6cf3c971
JP
839};
840
841struct mlxsw_sp_neigh_entry {
9665b745 842 struct list_head rif_list_node;
6cf3c971
JP
843 struct rhash_head ht_node;
844 struct mlxsw_sp_neigh_key key;
845 u16 rif;
5c8802f1 846 bool connected;
a6bf9e93 847 unsigned char ha[ETH_ALEN];
a7ff87ac
JP
848 struct list_head nexthop_list; /* list of nexthops using
849 * this neigh entry
850 */
b2157149 851 struct list_head nexthop_neighs_list_node;
6cf3c971
JP
852};
853
854static const struct rhashtable_params mlxsw_sp_neigh_ht_params = {
855 .key_offset = offsetof(struct mlxsw_sp_neigh_entry, key),
856 .head_offset = offsetof(struct mlxsw_sp_neigh_entry, ht_node),
857 .key_len = sizeof(struct mlxsw_sp_neigh_key),
858};
859
6cf3c971 860static struct mlxsw_sp_neigh_entry *
5c8802f1
IS
861mlxsw_sp_neigh_entry_alloc(struct mlxsw_sp *mlxsw_sp, struct neighbour *n,
862 u16 rif)
6cf3c971
JP
863{
864 struct mlxsw_sp_neigh_entry *neigh_entry;
865
5c8802f1 866 neigh_entry = kzalloc(sizeof(*neigh_entry), GFP_KERNEL);
6cf3c971
JP
867 if (!neigh_entry)
868 return NULL;
5c8802f1 869
33b1341c 870 neigh_entry->key.n = n;
6cf3c971 871 neigh_entry->rif = rif;
a7ff87ac 872 INIT_LIST_HEAD(&neigh_entry->nexthop_list);
5c8802f1 873
6cf3c971
JP
874 return neigh_entry;
875}
876
5c8802f1 877static void mlxsw_sp_neigh_entry_free(struct mlxsw_sp_neigh_entry *neigh_entry)
6cf3c971
JP
878{
879 kfree(neigh_entry);
880}
881
5c8802f1
IS
882static int
883mlxsw_sp_neigh_entry_insert(struct mlxsw_sp *mlxsw_sp,
884 struct mlxsw_sp_neigh_entry *neigh_entry)
6cf3c971 885{
9011b677 886 return rhashtable_insert_fast(&mlxsw_sp->router->neigh_ht,
5c8802f1
IS
887 &neigh_entry->ht_node,
888 mlxsw_sp_neigh_ht_params);
889}
6cf3c971 890
5c8802f1
IS
891static void
892mlxsw_sp_neigh_entry_remove(struct mlxsw_sp *mlxsw_sp,
893 struct mlxsw_sp_neigh_entry *neigh_entry)
894{
9011b677 895 rhashtable_remove_fast(&mlxsw_sp->router->neigh_ht,
5c8802f1
IS
896 &neigh_entry->ht_node,
897 mlxsw_sp_neigh_ht_params);
6cf3c971
JP
898}
899
5c8802f1
IS
900static struct mlxsw_sp_neigh_entry *
901mlxsw_sp_neigh_entry_create(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
6cf3c971 902{
6cf3c971 903 struct mlxsw_sp_neigh_entry *neigh_entry;
bf95233e 904 struct mlxsw_sp_rif *rif;
6cf3c971
JP
905 int err;
906
bf95233e
AS
907 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, n->dev);
908 if (!rif)
5c8802f1 909 return ERR_PTR(-EINVAL);
6cf3c971 910
bf95233e 911 neigh_entry = mlxsw_sp_neigh_entry_alloc(mlxsw_sp, n, rif->rif_index);
6cf3c971 912 if (!neigh_entry)
5c8802f1
IS
913 return ERR_PTR(-ENOMEM);
914
6cf3c971
JP
915 err = mlxsw_sp_neigh_entry_insert(mlxsw_sp, neigh_entry);
916 if (err)
917 goto err_neigh_entry_insert;
5c8802f1 918
bf95233e 919 list_add(&neigh_entry->rif_list_node, &rif->neigh_list);
9665b745 920
5c8802f1 921 return neigh_entry;
6cf3c971
JP
922
923err_neigh_entry_insert:
5c8802f1
IS
924 mlxsw_sp_neigh_entry_free(neigh_entry);
925 return ERR_PTR(err);
6cf3c971
JP
926}
927
5c8802f1
IS
928static void
929mlxsw_sp_neigh_entry_destroy(struct mlxsw_sp *mlxsw_sp,
930 struct mlxsw_sp_neigh_entry *neigh_entry)
6cf3c971 931{
9665b745 932 list_del(&neigh_entry->rif_list_node);
5c8802f1
IS
933 mlxsw_sp_neigh_entry_remove(mlxsw_sp, neigh_entry);
934 mlxsw_sp_neigh_entry_free(neigh_entry);
935}
6cf3c971 936
5c8802f1
IS
937static struct mlxsw_sp_neigh_entry *
938mlxsw_sp_neigh_entry_lookup(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
939{
940 struct mlxsw_sp_neigh_key key;
6cf3c971 941
5c8802f1 942 key.n = n;
9011b677 943 return rhashtable_lookup_fast(&mlxsw_sp->router->neigh_ht,
5c8802f1 944 &key, mlxsw_sp_neigh_ht_params);
6cf3c971
JP
945}
946
c723c735
YG
947static void
948mlxsw_sp_router_neighs_update_interval_init(struct mlxsw_sp *mlxsw_sp)
949{
a6c9b5d1 950 unsigned long interval;
c723c735 951
b5f3e0d4 952#if IS_ENABLED(CONFIG_IPV6)
a6c9b5d1
AS
953 interval = min_t(unsigned long,
954 NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME),
955 NEIGH_VAR(&nd_tbl.parms, DELAY_PROBE_TIME));
b5f3e0d4
IS
956#else
957 interval = NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME);
958#endif
9011b677 959 mlxsw_sp->router->neighs_update.interval = jiffies_to_msecs(interval);
c723c735
YG
960}
961
962static void mlxsw_sp_router_neigh_ent_ipv4_process(struct mlxsw_sp *mlxsw_sp,
963 char *rauhtd_pl,
964 int ent_index)
965{
966 struct net_device *dev;
967 struct neighbour *n;
968 __be32 dipn;
969 u32 dip;
970 u16 rif;
971
972 mlxsw_reg_rauhtd_ent_ipv4_unpack(rauhtd_pl, ent_index, &rif, &dip);
973
5f9efffb 974 if (!mlxsw_sp->router->rifs[rif]) {
c723c735
YG
975 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
976 return;
977 }
978
979 dipn = htonl(dip);
5f9efffb 980 dev = mlxsw_sp->router->rifs[rif]->dev;
c723c735
YG
981 n = neigh_lookup(&arp_tbl, &dipn, dev);
982 if (!n) {
983 netdev_err(dev, "Failed to find matching neighbour for IP=%pI4h\n",
984 &dip);
985 return;
986 }
987
988 netdev_dbg(dev, "Updating neighbour with IP=%pI4h\n", &dip);
989 neigh_event_send(n, NULL);
990 neigh_release(n);
991}
992
b5f3e0d4 993#if IS_ENABLED(IPV6)
60f040ca
AS
994static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
995 char *rauhtd_pl,
996 int rec_index)
997{
998 struct net_device *dev;
999 struct neighbour *n;
1000 struct in6_addr dip;
1001 u16 rif;
1002
1003 mlxsw_reg_rauhtd_ent_ipv6_unpack(rauhtd_pl, rec_index, &rif,
1004 (char *) &dip);
1005
1006 if (!mlxsw_sp->router->rifs[rif]) {
1007 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
1008 return;
1009 }
1010
1011 dev = mlxsw_sp->router->rifs[rif]->dev;
1012 n = neigh_lookup(&nd_tbl, &dip, dev);
1013 if (!n) {
1014 netdev_err(dev, "Failed to find matching neighbour for IP=%pI6c\n",
1015 &dip);
1016 return;
1017 }
1018
1019 netdev_dbg(dev, "Updating neighbour with IP=%pI6c\n", &dip);
1020 neigh_event_send(n, NULL);
1021 neigh_release(n);
1022}
b5f3e0d4
IS
1023#else
1024static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1025 char *rauhtd_pl,
1026 int rec_index)
1027{
1028}
1029#endif
60f040ca 1030
c723c735
YG
1031static void mlxsw_sp_router_neigh_rec_ipv4_process(struct mlxsw_sp *mlxsw_sp,
1032 char *rauhtd_pl,
1033 int rec_index)
1034{
1035 u8 num_entries;
1036 int i;
1037
1038 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
1039 rec_index);
1040 /* Hardware starts counting at 0, so add 1. */
1041 num_entries++;
1042
1043 /* Each record consists of several neighbour entries. */
1044 for (i = 0; i < num_entries; i++) {
1045 int ent_index;
1046
1047 ent_index = rec_index * MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC + i;
1048 mlxsw_sp_router_neigh_ent_ipv4_process(mlxsw_sp, rauhtd_pl,
1049 ent_index);
1050 }
1051
1052}
1053
60f040ca
AS
1054static void mlxsw_sp_router_neigh_rec_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1055 char *rauhtd_pl,
1056 int rec_index)
1057{
1058 /* One record contains one entry. */
1059 mlxsw_sp_router_neigh_ent_ipv6_process(mlxsw_sp, rauhtd_pl,
1060 rec_index);
1061}
1062
c723c735
YG
1063static void mlxsw_sp_router_neigh_rec_process(struct mlxsw_sp *mlxsw_sp,
1064 char *rauhtd_pl, int rec_index)
1065{
1066 switch (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, rec_index)) {
1067 case MLXSW_REG_RAUHTD_TYPE_IPV4:
1068 mlxsw_sp_router_neigh_rec_ipv4_process(mlxsw_sp, rauhtd_pl,
1069 rec_index);
1070 break;
1071 case MLXSW_REG_RAUHTD_TYPE_IPV6:
60f040ca
AS
1072 mlxsw_sp_router_neigh_rec_ipv6_process(mlxsw_sp, rauhtd_pl,
1073 rec_index);
c723c735
YG
1074 break;
1075 }
1076}
1077
42cdb338
AS
1078static bool mlxsw_sp_router_rauhtd_is_full(char *rauhtd_pl)
1079{
1080 u8 num_rec, last_rec_index, num_entries;
1081
1082 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
1083 last_rec_index = num_rec - 1;
1084
1085 if (num_rec < MLXSW_REG_RAUHTD_REC_MAX_NUM)
1086 return false;
1087 if (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, last_rec_index) ==
1088 MLXSW_REG_RAUHTD_TYPE_IPV6)
1089 return true;
1090
1091 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
1092 last_rec_index);
1093 if (++num_entries == MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC)
1094 return true;
1095 return false;
1096}
1097
60f040ca
AS
1098static int
1099__mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp,
1100 char *rauhtd_pl,
1101 enum mlxsw_reg_rauhtd_type type)
c723c735 1102{
60f040ca
AS
1103 int i, num_rec;
1104 int err;
c723c735
YG
1105
1106 /* Make sure the neighbour's netdev isn't removed in the
1107 * process.
1108 */
1109 rtnl_lock();
1110 do {
60f040ca 1111 mlxsw_reg_rauhtd_pack(rauhtd_pl, type);
c723c735
YG
1112 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(rauhtd),
1113 rauhtd_pl);
1114 if (err) {
1115 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to dump neighbour talbe\n");
1116 break;
1117 }
1118 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
1119 for (i = 0; i < num_rec; i++)
1120 mlxsw_sp_router_neigh_rec_process(mlxsw_sp, rauhtd_pl,
1121 i);
42cdb338 1122 } while (mlxsw_sp_router_rauhtd_is_full(rauhtd_pl));
c723c735
YG
1123 rtnl_unlock();
1124
60f040ca
AS
1125 return err;
1126}
1127
1128static int mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp)
1129{
1130 enum mlxsw_reg_rauhtd_type type;
1131 char *rauhtd_pl;
1132 int err;
1133
1134 rauhtd_pl = kmalloc(MLXSW_REG_RAUHTD_LEN, GFP_KERNEL);
1135 if (!rauhtd_pl)
1136 return -ENOMEM;
1137
1138 type = MLXSW_REG_RAUHTD_TYPE_IPV4;
1139 err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);
1140 if (err)
1141 goto out;
1142
1143 type = MLXSW_REG_RAUHTD_TYPE_IPV6;
1144 err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);
1145out:
c723c735 1146 kfree(rauhtd_pl);
b2157149
YG
1147 return err;
1148}
1149
1150static void mlxsw_sp_router_neighs_update_nh(struct mlxsw_sp *mlxsw_sp)
1151{
1152 struct mlxsw_sp_neigh_entry *neigh_entry;
1153
1154 /* Take RTNL mutex here to prevent lists from changes */
1155 rtnl_lock();
9011b677 1156 list_for_each_entry(neigh_entry, &mlxsw_sp->router->nexthop_neighs_list,
8a0b7275 1157 nexthop_neighs_list_node)
b2157149
YG
1158 /* If this neigh have nexthops, make the kernel think this neigh
1159 * is active regardless of the traffic.
1160 */
8a0b7275 1161 neigh_event_send(neigh_entry->key.n, NULL);
b2157149
YG
1162 rtnl_unlock();
1163}
1164
1165static void
1166mlxsw_sp_router_neighs_update_work_schedule(struct mlxsw_sp *mlxsw_sp)
1167{
9011b677 1168 unsigned long interval = mlxsw_sp->router->neighs_update.interval;
b2157149 1169
9011b677 1170 mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw,
b2157149
YG
1171 msecs_to_jiffies(interval));
1172}
1173
1174static void mlxsw_sp_router_neighs_update_work(struct work_struct *work)
1175{
9011b677 1176 struct mlxsw_sp_router *router;
b2157149
YG
1177 int err;
1178
9011b677
IS
1179 router = container_of(work, struct mlxsw_sp_router,
1180 neighs_update.dw.work);
1181 err = mlxsw_sp_router_neighs_update_rauhtd(router->mlxsw_sp);
b2157149 1182 if (err)
9011b677 1183 dev_err(router->mlxsw_sp->bus_info->dev, "Could not update kernel for neigh activity");
b2157149 1184
9011b677 1185 mlxsw_sp_router_neighs_update_nh(router->mlxsw_sp);
b2157149 1186
9011b677 1187 mlxsw_sp_router_neighs_update_work_schedule(router->mlxsw_sp);
c723c735
YG
1188}
1189
0b2361d9
YG
1190static void mlxsw_sp_router_probe_unresolved_nexthops(struct work_struct *work)
1191{
1192 struct mlxsw_sp_neigh_entry *neigh_entry;
9011b677 1193 struct mlxsw_sp_router *router;
0b2361d9 1194
9011b677
IS
1195 router = container_of(work, struct mlxsw_sp_router,
1196 nexthop_probe_dw.work);
0b2361d9
YG
1197 /* Iterate over nexthop neighbours, find those who are unresolved and
1198 * send arp on them. This solves the chicken-egg problem when
1199 * the nexthop wouldn't get offloaded until the neighbor is resolved
1200 * but it wouldn't get resolved ever in case traffic is flowing in HW
1201 * using different nexthop.
1202 *
1203 * Take RTNL mutex here to prevent lists from changes.
1204 */
1205 rtnl_lock();
9011b677 1206 list_for_each_entry(neigh_entry, &router->nexthop_neighs_list,
8a0b7275 1207 nexthop_neighs_list_node)
01b1aa35 1208 if (!neigh_entry->connected)
33b1341c 1209 neigh_event_send(neigh_entry->key.n, NULL);
0b2361d9
YG
1210 rtnl_unlock();
1211
9011b677 1212 mlxsw_core_schedule_dw(&router->nexthop_probe_dw,
0b2361d9
YG
1213 MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL);
1214}
1215
a7ff87ac
JP
1216static void
1217mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
1218 struct mlxsw_sp_neigh_entry *neigh_entry,
1219 bool removing);
1220
5c8802f1
IS
1221static enum mlxsw_reg_rauht_op mlxsw_sp_rauht_op(bool adding)
1222{
1223 return adding ? MLXSW_REG_RAUHT_OP_WRITE_ADD :
1224 MLXSW_REG_RAUHT_OP_WRITE_DELETE;
1225}
1226
1227static void
1228mlxsw_sp_router_neigh_entry_op4(struct mlxsw_sp *mlxsw_sp,
1229 struct mlxsw_sp_neigh_entry *neigh_entry,
1230 enum mlxsw_reg_rauht_op op)
a6bf9e93 1231{
33b1341c 1232 struct neighbour *n = neigh_entry->key.n;
5c8802f1 1233 u32 dip = ntohl(*((__be32 *) n->primary_key));
a6bf9e93 1234 char rauht_pl[MLXSW_REG_RAUHT_LEN];
5c8802f1
IS
1235
1236 mlxsw_reg_rauht_pack4(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
1237 dip);
1238 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
1239}
1240
d5eb89cf
AS
1241static void
1242mlxsw_sp_router_neigh_entry_op6(struct mlxsw_sp *mlxsw_sp,
1243 struct mlxsw_sp_neigh_entry *neigh_entry,
1244 enum mlxsw_reg_rauht_op op)
1245{
1246 struct neighbour *n = neigh_entry->key.n;
1247 char rauht_pl[MLXSW_REG_RAUHT_LEN];
1248 const char *dip = n->primary_key;
1249
1250 mlxsw_reg_rauht_pack6(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
1251 dip);
1252 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
1253}
1254
1255static bool mlxsw_sp_neigh_ipv6_ignore(struct neighbour *n)
1256{
1257 /* Packets with a link-local destination address are trapped
1258 * after LPM lookup and never reach the neighbour table, so
1259 * there is no need to program such neighbours to the device.
1260 */
1261 if (ipv6_addr_type((struct in6_addr *) &n->primary_key) &
1262 IPV6_ADDR_LINKLOCAL)
1263 return true;
1264 return false;
1265}
1266
5c8802f1
IS
1267static void
1268mlxsw_sp_neigh_entry_update(struct mlxsw_sp *mlxsw_sp,
1269 struct mlxsw_sp_neigh_entry *neigh_entry,
1270 bool adding)
1271{
1272 if (!adding && !neigh_entry->connected)
1273 return;
1274 neigh_entry->connected = adding;
b5f3e0d4 1275 if (neigh_entry->key.n->tbl->family == AF_INET) {
5c8802f1
IS
1276 mlxsw_sp_router_neigh_entry_op4(mlxsw_sp, neigh_entry,
1277 mlxsw_sp_rauht_op(adding));
b5f3e0d4 1278 } else if (neigh_entry->key.n->tbl->family == AF_INET6) {
d5eb89cf
AS
1279 if (mlxsw_sp_neigh_ipv6_ignore(neigh_entry->key.n))
1280 return;
1281 mlxsw_sp_router_neigh_entry_op6(mlxsw_sp, neigh_entry,
1282 mlxsw_sp_rauht_op(adding));
1283 } else {
5c8802f1 1284 WARN_ON_ONCE(1);
d5eb89cf 1285 }
5c8802f1
IS
1286}
1287
1288struct mlxsw_sp_neigh_event_work {
1289 struct work_struct work;
1290 struct mlxsw_sp *mlxsw_sp;
1291 struct neighbour *n;
1292};
1293
1294static void mlxsw_sp_router_neigh_event_work(struct work_struct *work)
1295{
1296 struct mlxsw_sp_neigh_event_work *neigh_work =
1297 container_of(work, struct mlxsw_sp_neigh_event_work, work);
1298 struct mlxsw_sp *mlxsw_sp = neigh_work->mlxsw_sp;
1299 struct mlxsw_sp_neigh_entry *neigh_entry;
1300 struct neighbour *n = neigh_work->n;
1301 unsigned char ha[ETH_ALEN];
a6bf9e93 1302 bool entry_connected;
93a87e5e 1303 u8 nud_state, dead;
a6bf9e93 1304
5c8802f1
IS
1305 /* If these parameters are changed after we release the lock,
1306 * then we are guaranteed to receive another event letting us
1307 * know about it.
1308 */
a6bf9e93 1309 read_lock_bh(&n->lock);
5c8802f1 1310 memcpy(ha, n->ha, ETH_ALEN);
a6bf9e93 1311 nud_state = n->nud_state;
93a87e5e 1312 dead = n->dead;
a6bf9e93
YG
1313 read_unlock_bh(&n->lock);
1314
5c8802f1 1315 rtnl_lock();
93a87e5e 1316 entry_connected = nud_state & NUD_VALID && !dead;
5c8802f1
IS
1317 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
1318 if (!entry_connected && !neigh_entry)
1319 goto out;
1320 if (!neigh_entry) {
1321 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
1322 if (IS_ERR(neigh_entry))
1323 goto out;
a6bf9e93
YG
1324 }
1325
5c8802f1
IS
1326 memcpy(neigh_entry->ha, ha, ETH_ALEN);
1327 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, entry_connected);
1328 mlxsw_sp_nexthop_neigh_update(mlxsw_sp, neigh_entry, !entry_connected);
1329
1330 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
1331 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
1332
1333out:
1334 rtnl_unlock();
a6bf9e93 1335 neigh_release(n);
5c8802f1 1336 kfree(neigh_work);
a6bf9e93
YG
1337}
1338
e7322638
JP
1339int mlxsw_sp_router_netevent_event(struct notifier_block *unused,
1340 unsigned long event, void *ptr)
c723c735 1341{
5c8802f1 1342 struct mlxsw_sp_neigh_event_work *neigh_work;
c723c735
YG
1343 struct mlxsw_sp_port *mlxsw_sp_port;
1344 struct mlxsw_sp *mlxsw_sp;
1345 unsigned long interval;
1346 struct neigh_parms *p;
a6bf9e93 1347 struct neighbour *n;
c723c735
YG
1348
1349 switch (event) {
1350 case NETEVENT_DELAY_PROBE_TIME_UPDATE:
1351 p = ptr;
1352
1353 /* We don't care about changes in the default table. */
b5f3e0d4
IS
1354 if (!p->dev || (p->tbl->family != AF_INET &&
1355 p->tbl->family != AF_INET6))
c723c735
YG
1356 return NOTIFY_DONE;
1357
1358 /* We are in atomic context and can't take RTNL mutex,
1359 * so use RCU variant to walk the device chain.
1360 */
1361 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(p->dev);
1362 if (!mlxsw_sp_port)
1363 return NOTIFY_DONE;
1364
1365 mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1366 interval = jiffies_to_msecs(NEIGH_VAR(p, DELAY_PROBE_TIME));
9011b677 1367 mlxsw_sp->router->neighs_update.interval = interval;
c723c735
YG
1368
1369 mlxsw_sp_port_dev_put(mlxsw_sp_port);
1370 break;
a6bf9e93
YG
1371 case NETEVENT_NEIGH_UPDATE:
1372 n = ptr;
a6bf9e93 1373
b5f3e0d4 1374 if (n->tbl->family != AF_INET && n->tbl->family != AF_INET6)
a6bf9e93
YG
1375 return NOTIFY_DONE;
1376
5c8802f1 1377 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(n->dev);
a6bf9e93
YG
1378 if (!mlxsw_sp_port)
1379 return NOTIFY_DONE;
1380
5c8802f1
IS
1381 neigh_work = kzalloc(sizeof(*neigh_work), GFP_ATOMIC);
1382 if (!neigh_work) {
a6bf9e93 1383 mlxsw_sp_port_dev_put(mlxsw_sp_port);
5c8802f1 1384 return NOTIFY_BAD;
a6bf9e93 1385 }
5c8802f1
IS
1386
1387 INIT_WORK(&neigh_work->work, mlxsw_sp_router_neigh_event_work);
1388 neigh_work->mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1389 neigh_work->n = n;
a6bf9e93
YG
1390
1391 /* Take a reference to ensure the neighbour won't be
1392 * destructed until we drop the reference in delayed
1393 * work.
1394 */
1395 neigh_clone(n);
5c8802f1
IS
1396 mlxsw_core_schedule_work(&neigh_work->work);
1397 mlxsw_sp_port_dev_put(mlxsw_sp_port);
a6bf9e93 1398 break;
c723c735
YG
1399 }
1400
1401 return NOTIFY_DONE;
1402}
1403
6cf3c971
JP
1404static int mlxsw_sp_neigh_init(struct mlxsw_sp *mlxsw_sp)
1405{
c723c735
YG
1406 int err;
1407
9011b677 1408 err = rhashtable_init(&mlxsw_sp->router->neigh_ht,
c723c735
YG
1409 &mlxsw_sp_neigh_ht_params);
1410 if (err)
1411 return err;
1412
1413 /* Initialize the polling interval according to the default
1414 * table.
1415 */
1416 mlxsw_sp_router_neighs_update_interval_init(mlxsw_sp);
1417
0b2361d9 1418 /* Create the delayed works for the activity_update */
9011b677 1419 INIT_DELAYED_WORK(&mlxsw_sp->router->neighs_update.dw,
c723c735 1420 mlxsw_sp_router_neighs_update_work);
9011b677 1421 INIT_DELAYED_WORK(&mlxsw_sp->router->nexthop_probe_dw,
0b2361d9 1422 mlxsw_sp_router_probe_unresolved_nexthops);
9011b677
IS
1423 mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw, 0);
1424 mlxsw_core_schedule_dw(&mlxsw_sp->router->nexthop_probe_dw, 0);
c723c735 1425 return 0;
6cf3c971
JP
1426}
1427
1428static void mlxsw_sp_neigh_fini(struct mlxsw_sp *mlxsw_sp)
1429{
9011b677
IS
1430 cancel_delayed_work_sync(&mlxsw_sp->router->neighs_update.dw);
1431 cancel_delayed_work_sync(&mlxsw_sp->router->nexthop_probe_dw);
1432 rhashtable_destroy(&mlxsw_sp->router->neigh_ht);
6cf3c971
JP
1433}
1434
9665b745 1435static void mlxsw_sp_neigh_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
bf95233e 1436 struct mlxsw_sp_rif *rif)
9665b745
IS
1437{
1438 struct mlxsw_sp_neigh_entry *neigh_entry, *tmp;
1439
bf95233e 1440 list_for_each_entry_safe(neigh_entry, tmp, &rif->neigh_list,
4a3c67a6
IS
1441 rif_list_node) {
1442 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, false);
9665b745 1443 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
4a3c67a6 1444 }
9665b745
IS
1445}
1446
c53b8e1b
IS
1447struct mlxsw_sp_nexthop_key {
1448 struct fib_nh *fib_nh;
1449};
1450
a7ff87ac
JP
1451struct mlxsw_sp_nexthop {
1452 struct list_head neigh_list_node; /* member of neigh entry list */
9665b745 1453 struct list_head rif_list_node;
a7ff87ac
JP
1454 struct mlxsw_sp_nexthop_group *nh_grp; /* pointer back to the group
1455 * this belongs to
1456 */
c53b8e1b
IS
1457 struct rhash_head ht_node;
1458 struct mlxsw_sp_nexthop_key key;
58adf2c4 1459 unsigned char gw_addr[sizeof(struct in6_addr)];
bf95233e 1460 struct mlxsw_sp_rif *rif;
a7ff87ac
JP
1461 u8 should_offload:1, /* set indicates this neigh is connected and
1462 * should be put to KVD linear area of this group.
1463 */
1464 offloaded:1, /* set in case the neigh is actually put into
1465 * KVD linear area of this group.
1466 */
1467 update:1; /* set indicates that MAC of this neigh should be
1468 * updated in HW
1469 */
1470 struct mlxsw_sp_neigh_entry *neigh_entry;
1471};
1472
e9ad5e7d
IS
1473struct mlxsw_sp_nexthop_group_key {
1474 struct fib_info *fi;
1475};
1476
a7ff87ac 1477struct mlxsw_sp_nexthop_group {
e9ad5e7d 1478 struct rhash_head ht_node;
a7ff87ac 1479 struct list_head fib_list; /* list of fib entries that use this group */
58adf2c4 1480 struct neigh_table *neigh_tbl;
e9ad5e7d 1481 struct mlxsw_sp_nexthop_group_key key;
b3e8d1eb
IS
1482 u8 adj_index_valid:1,
1483 gateway:1; /* routes using the group use a gateway */
a7ff87ac
JP
1484 u32 adj_index;
1485 u16 ecmp_size;
1486 u16 count;
1487 struct mlxsw_sp_nexthop nexthops[0];
bf95233e 1488#define nh_rif nexthops[0].rif
a7ff87ac
JP
1489};
1490
e9ad5e7d
IS
1491static const struct rhashtable_params mlxsw_sp_nexthop_group_ht_params = {
1492 .key_offset = offsetof(struct mlxsw_sp_nexthop_group, key),
1493 .head_offset = offsetof(struct mlxsw_sp_nexthop_group, ht_node),
1494 .key_len = sizeof(struct mlxsw_sp_nexthop_group_key),
1495};
1496
1497static int mlxsw_sp_nexthop_group_insert(struct mlxsw_sp *mlxsw_sp,
1498 struct mlxsw_sp_nexthop_group *nh_grp)
1499{
9011b677 1500 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_group_ht,
e9ad5e7d
IS
1501 &nh_grp->ht_node,
1502 mlxsw_sp_nexthop_group_ht_params);
1503}
1504
1505static void mlxsw_sp_nexthop_group_remove(struct mlxsw_sp *mlxsw_sp,
1506 struct mlxsw_sp_nexthop_group *nh_grp)
1507{
9011b677 1508 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_group_ht,
e9ad5e7d
IS
1509 &nh_grp->ht_node,
1510 mlxsw_sp_nexthop_group_ht_params);
1511}
1512
1513static struct mlxsw_sp_nexthop_group *
1514mlxsw_sp_nexthop_group_lookup(struct mlxsw_sp *mlxsw_sp,
1515 struct mlxsw_sp_nexthop_group_key key)
1516{
9011b677 1517 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht, &key,
e9ad5e7d
IS
1518 mlxsw_sp_nexthop_group_ht_params);
1519}
1520
c53b8e1b
IS
1521static const struct rhashtable_params mlxsw_sp_nexthop_ht_params = {
1522 .key_offset = offsetof(struct mlxsw_sp_nexthop, key),
1523 .head_offset = offsetof(struct mlxsw_sp_nexthop, ht_node),
1524 .key_len = sizeof(struct mlxsw_sp_nexthop_key),
1525};
1526
1527static int mlxsw_sp_nexthop_insert(struct mlxsw_sp *mlxsw_sp,
1528 struct mlxsw_sp_nexthop *nh)
1529{
9011b677 1530 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_ht,
c53b8e1b
IS
1531 &nh->ht_node, mlxsw_sp_nexthop_ht_params);
1532}
1533
1534static void mlxsw_sp_nexthop_remove(struct mlxsw_sp *mlxsw_sp,
1535 struct mlxsw_sp_nexthop *nh)
1536{
9011b677 1537 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_ht, &nh->ht_node,
c53b8e1b
IS
1538 mlxsw_sp_nexthop_ht_params);
1539}
1540
ad178c8e
IS
1541static struct mlxsw_sp_nexthop *
1542mlxsw_sp_nexthop_lookup(struct mlxsw_sp *mlxsw_sp,
1543 struct mlxsw_sp_nexthop_key key)
1544{
9011b677 1545 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_ht, &key,
ad178c8e
IS
1546 mlxsw_sp_nexthop_ht_params);
1547}
1548
a7ff87ac 1549static int mlxsw_sp_adj_index_mass_update_vr(struct mlxsw_sp *mlxsw_sp,
76610ebb 1550 const struct mlxsw_sp_fib *fib,
a7ff87ac
JP
1551 u32 adj_index, u16 ecmp_size,
1552 u32 new_adj_index,
1553 u16 new_ecmp_size)
1554{
1555 char raleu_pl[MLXSW_REG_RALEU_LEN];
1556
1a9234e6 1557 mlxsw_reg_raleu_pack(raleu_pl,
76610ebb
IS
1558 (enum mlxsw_reg_ralxx_protocol) fib->proto,
1559 fib->vr->id, adj_index, ecmp_size, new_adj_index,
1a9234e6 1560 new_ecmp_size);
a7ff87ac
JP
1561 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raleu), raleu_pl);
1562}
1563
1564static int mlxsw_sp_adj_index_mass_update(struct mlxsw_sp *mlxsw_sp,
1565 struct mlxsw_sp_nexthop_group *nh_grp,
1566 u32 old_adj_index, u16 old_ecmp_size)
1567{
1568 struct mlxsw_sp_fib_entry *fib_entry;
76610ebb 1569 struct mlxsw_sp_fib *fib = NULL;
a7ff87ac
JP
1570 int err;
1571
1572 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
76610ebb 1573 if (fib == fib_entry->fib_node->fib)
a7ff87ac 1574 continue;
76610ebb
IS
1575 fib = fib_entry->fib_node->fib;
1576 err = mlxsw_sp_adj_index_mass_update_vr(mlxsw_sp, fib,
a7ff87ac
JP
1577 old_adj_index,
1578 old_ecmp_size,
1579 nh_grp->adj_index,
1580 nh_grp->ecmp_size);
1581 if (err)
1582 return err;
1583 }
1584 return 0;
1585}
1586
1587static int mlxsw_sp_nexthop_mac_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index,
1588 struct mlxsw_sp_nexthop *nh)
1589{
1590 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
1591 char ratr_pl[MLXSW_REG_RATR_LEN];
1592
1593 mlxsw_reg_ratr_pack(ratr_pl, MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY,
1594 true, adj_index, neigh_entry->rif);
1595 mlxsw_reg_ratr_eth_entry_pack(ratr_pl, neigh_entry->ha);
1596 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl);
1597}
1598
1599static int
1600mlxsw_sp_nexthop_group_mac_update(struct mlxsw_sp *mlxsw_sp,
a59b7e02
IS
1601 struct mlxsw_sp_nexthop_group *nh_grp,
1602 bool reallocate)
a7ff87ac
JP
1603{
1604 u32 adj_index = nh_grp->adj_index; /* base */
1605 struct mlxsw_sp_nexthop *nh;
1606 int i;
1607 int err;
1608
1609 for (i = 0; i < nh_grp->count; i++) {
1610 nh = &nh_grp->nexthops[i];
1611
1612 if (!nh->should_offload) {
1613 nh->offloaded = 0;
1614 continue;
1615 }
1616
a59b7e02 1617 if (nh->update || reallocate) {
a7ff87ac
JP
1618 err = mlxsw_sp_nexthop_mac_update(mlxsw_sp,
1619 adj_index, nh);
1620 if (err)
1621 return err;
1622 nh->update = 0;
1623 nh->offloaded = 1;
1624 }
1625 adj_index++;
1626 }
1627 return 0;
1628}
1629
1630static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
1631 struct mlxsw_sp_fib_entry *fib_entry);
1632
1819ae3d
IS
1633static bool
1634mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
1635 const struct mlxsw_sp_fib_entry *fib_entry);
1636
a7ff87ac
JP
1637static int
1638mlxsw_sp_nexthop_fib_entries_update(struct mlxsw_sp *mlxsw_sp,
1639 struct mlxsw_sp_nexthop_group *nh_grp)
1640{
1641 struct mlxsw_sp_fib_entry *fib_entry;
1642 int err;
1643
1644 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
1819ae3d
IS
1645 if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node,
1646 fib_entry))
1647 continue;
a7ff87ac
JP
1648 err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
1649 if (err)
1650 return err;
1651 }
1652 return 0;
1653}
1654
1655static void
1656mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp,
1657 struct mlxsw_sp_nexthop_group *nh_grp)
1658{
1659 struct mlxsw_sp_nexthop *nh;
1660 bool offload_change = false;
1661 u32 adj_index;
1662 u16 ecmp_size = 0;
1663 bool old_adj_index_valid;
1664 u32 old_adj_index;
1665 u16 old_ecmp_size;
a7ff87ac
JP
1666 int i;
1667 int err;
1668
b3e8d1eb
IS
1669 if (!nh_grp->gateway) {
1670 mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
1671 return;
1672 }
1673
a7ff87ac
JP
1674 for (i = 0; i < nh_grp->count; i++) {
1675 nh = &nh_grp->nexthops[i];
1676
56b8a9ed 1677 if (nh->should_offload != nh->offloaded) {
a7ff87ac
JP
1678 offload_change = true;
1679 if (nh->should_offload)
1680 nh->update = 1;
1681 }
1682 if (nh->should_offload)
1683 ecmp_size++;
1684 }
1685 if (!offload_change) {
1686 /* Nothing was added or removed, so no need to reallocate. Just
1687 * update MAC on existing adjacency indexes.
1688 */
a59b7e02
IS
1689 err = mlxsw_sp_nexthop_group_mac_update(mlxsw_sp, nh_grp,
1690 false);
a7ff87ac
JP
1691 if (err) {
1692 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
1693 goto set_trap;
1694 }
1695 return;
1696 }
1697 if (!ecmp_size)
1698 /* No neigh of this group is connected so we just set
1699 * the trap and let everthing flow through kernel.
1700 */
1701 goto set_trap;
1702
13124443
AS
1703 err = mlxsw_sp_kvdl_alloc(mlxsw_sp, ecmp_size, &adj_index);
1704 if (err) {
a7ff87ac
JP
1705 /* We ran out of KVD linear space, just set the
1706 * trap and let everything flow through kernel.
1707 */
1708 dev_warn(mlxsw_sp->bus_info->dev, "Failed to allocate KVD linear area for nexthop group.\n");
1709 goto set_trap;
1710 }
a7ff87ac
JP
1711 old_adj_index_valid = nh_grp->adj_index_valid;
1712 old_adj_index = nh_grp->adj_index;
1713 old_ecmp_size = nh_grp->ecmp_size;
1714 nh_grp->adj_index_valid = 1;
1715 nh_grp->adj_index = adj_index;
1716 nh_grp->ecmp_size = ecmp_size;
a59b7e02 1717 err = mlxsw_sp_nexthop_group_mac_update(mlxsw_sp, nh_grp, true);
a7ff87ac
JP
1718 if (err) {
1719 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
1720 goto set_trap;
1721 }
1722
1723 if (!old_adj_index_valid) {
1724 /* The trap was set for fib entries, so we have to call
1725 * fib entry update to unset it and use adjacency index.
1726 */
1727 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
1728 if (err) {
1729 dev_warn(mlxsw_sp->bus_info->dev, "Failed to add adjacency index to fib entries.\n");
1730 goto set_trap;
1731 }
1732 return;
1733 }
1734
1735 err = mlxsw_sp_adj_index_mass_update(mlxsw_sp, nh_grp,
1736 old_adj_index, old_ecmp_size);
1737 mlxsw_sp_kvdl_free(mlxsw_sp, old_adj_index);
1738 if (err) {
1739 dev_warn(mlxsw_sp->bus_info->dev, "Failed to mass-update adjacency index for nexthop group.\n");
1740 goto set_trap;
1741 }
1742 return;
1743
1744set_trap:
1745 old_adj_index_valid = nh_grp->adj_index_valid;
1746 nh_grp->adj_index_valid = 0;
1747 for (i = 0; i < nh_grp->count; i++) {
1748 nh = &nh_grp->nexthops[i];
1749 nh->offloaded = 0;
1750 }
1751 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
1752 if (err)
1753 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set traps for fib entries.\n");
1754 if (old_adj_index_valid)
1755 mlxsw_sp_kvdl_free(mlxsw_sp, nh_grp->adj_index);
1756}
1757
1758static void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh,
1759 bool removing)
1760{
213666a3 1761 if (!removing)
a7ff87ac 1762 nh->should_offload = 1;
213666a3 1763 else if (nh->offloaded)
a7ff87ac
JP
1764 nh->should_offload = 0;
1765 nh->update = 1;
1766}
1767
1768static void
1769mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
1770 struct mlxsw_sp_neigh_entry *neigh_entry,
1771 bool removing)
1772{
1773 struct mlxsw_sp_nexthop *nh;
1774
a7ff87ac
JP
1775 list_for_each_entry(nh, &neigh_entry->nexthop_list,
1776 neigh_list_node) {
1777 __mlxsw_sp_nexthop_neigh_update(nh, removing);
1778 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
1779 }
a7ff87ac
JP
1780}
1781
9665b745 1782static void mlxsw_sp_nexthop_rif_init(struct mlxsw_sp_nexthop *nh,
bf95233e 1783 struct mlxsw_sp_rif *rif)
9665b745 1784{
bf95233e 1785 if (nh->rif)
9665b745
IS
1786 return;
1787
bf95233e
AS
1788 nh->rif = rif;
1789 list_add(&nh->rif_list_node, &rif->nexthop_list);
9665b745
IS
1790}
1791
1792static void mlxsw_sp_nexthop_rif_fini(struct mlxsw_sp_nexthop *nh)
1793{
bf95233e 1794 if (!nh->rif)
9665b745
IS
1795 return;
1796
1797 list_del(&nh->rif_list_node);
bf95233e 1798 nh->rif = NULL;
9665b745
IS
1799}
1800
a8c97014
IS
1801static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp,
1802 struct mlxsw_sp_nexthop *nh)
a7ff87ac
JP
1803{
1804 struct mlxsw_sp_neigh_entry *neigh_entry;
a7ff87ac 1805 struct neighbour *n;
93a87e5e 1806 u8 nud_state, dead;
c53b8e1b
IS
1807 int err;
1808
ad178c8e 1809 if (!nh->nh_grp->gateway || nh->neigh_entry)
b8399a1e
IS
1810 return 0;
1811
33b1341c 1812 /* Take a reference of neigh here ensuring that neigh would
8de3c178 1813 * not be destructed before the nexthop entry is finished.
33b1341c 1814 * The reference is taken either in neigh_lookup() or
fd76d910 1815 * in neigh_create() in case n is not found.
33b1341c 1816 */
58adf2c4 1817 n = neigh_lookup(nh->nh_grp->neigh_tbl, &nh->gw_addr, nh->rif->dev);
33b1341c 1818 if (!n) {
58adf2c4
IS
1819 n = neigh_create(nh->nh_grp->neigh_tbl, &nh->gw_addr,
1820 nh->rif->dev);
a8c97014
IS
1821 if (IS_ERR(n))
1822 return PTR_ERR(n);
a7ff87ac 1823 neigh_event_send(n, NULL);
33b1341c
JP
1824 }
1825 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
1826 if (!neigh_entry) {
5c8802f1
IS
1827 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
1828 if (IS_ERR(neigh_entry)) {
c53b8e1b
IS
1829 err = -EINVAL;
1830 goto err_neigh_entry_create;
5c8802f1 1831 }
a7ff87ac 1832 }
b2157149
YG
1833
1834 /* If that is the first nexthop connected to that neigh, add to
1835 * nexthop_neighs_list
1836 */
1837 if (list_empty(&neigh_entry->nexthop_list))
1838 list_add_tail(&neigh_entry->nexthop_neighs_list_node,
9011b677 1839 &mlxsw_sp->router->nexthop_neighs_list);
b2157149 1840
a7ff87ac
JP
1841 nh->neigh_entry = neigh_entry;
1842 list_add_tail(&nh->neigh_list_node, &neigh_entry->nexthop_list);
1843 read_lock_bh(&n->lock);
1844 nud_state = n->nud_state;
93a87e5e 1845 dead = n->dead;
a7ff87ac 1846 read_unlock_bh(&n->lock);
93a87e5e 1847 __mlxsw_sp_nexthop_neigh_update(nh, !(nud_state & NUD_VALID && !dead));
a7ff87ac
JP
1848
1849 return 0;
c53b8e1b
IS
1850
1851err_neigh_entry_create:
1852 neigh_release(n);
c53b8e1b 1853 return err;
a7ff87ac
JP
1854}
1855
a8c97014
IS
1856static void mlxsw_sp_nexthop_neigh_fini(struct mlxsw_sp *mlxsw_sp,
1857 struct mlxsw_sp_nexthop *nh)
a7ff87ac
JP
1858{
1859 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
a8c97014 1860 struct neighbour *n;
a7ff87ac 1861
b8399a1e 1862 if (!neigh_entry)
a8c97014
IS
1863 return;
1864 n = neigh_entry->key.n;
b8399a1e 1865
58312125 1866 __mlxsw_sp_nexthop_neigh_update(nh, true);
a7ff87ac 1867 list_del(&nh->neigh_list_node);
e58be79e 1868 nh->neigh_entry = NULL;
b2157149
YG
1869
1870 /* If that is the last nexthop connected to that neigh, remove from
1871 * nexthop_neighs_list
1872 */
e58be79e
IS
1873 if (list_empty(&neigh_entry->nexthop_list))
1874 list_del(&neigh_entry->nexthop_neighs_list_node);
b2157149 1875
5c8802f1
IS
1876 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
1877 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
1878
1879 neigh_release(n);
a8c97014 1880}
c53b8e1b 1881
0e6ea2a4
IS
1882static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp,
1883 struct mlxsw_sp_nexthop_group *nh_grp,
1884 struct mlxsw_sp_nexthop *nh,
1885 struct fib_nh *fib_nh)
a8c97014
IS
1886{
1887 struct net_device *dev = fib_nh->nh_dev;
df6dd79b 1888 struct in_device *in_dev;
bf95233e 1889 struct mlxsw_sp_rif *rif;
a8c97014
IS
1890 int err;
1891
1892 nh->nh_grp = nh_grp;
1893 nh->key.fib_nh = fib_nh;
58adf2c4 1894 memcpy(&nh->gw_addr, &fib_nh->nh_gw, sizeof(fib_nh->nh_gw));
a8c97014
IS
1895 err = mlxsw_sp_nexthop_insert(mlxsw_sp, nh);
1896 if (err)
1897 return err;
1898
97989ee0
IS
1899 if (!dev)
1900 return 0;
1901
df6dd79b
IS
1902 in_dev = __in_dev_get_rtnl(dev);
1903 if (in_dev && IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) &&
1904 fib_nh->nh_flags & RTNH_F_LINKDOWN)
1905 return 0;
1906
bf95233e
AS
1907 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
1908 if (!rif)
a8c97014 1909 return 0;
bf95233e 1910 mlxsw_sp_nexthop_rif_init(nh, rif);
a8c97014
IS
1911
1912 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
1913 if (err)
1914 goto err_nexthop_neigh_init;
1915
1916 return 0;
1917
1918err_nexthop_neigh_init:
a4e75b76 1919 mlxsw_sp_nexthop_rif_fini(nh);
a8c97014
IS
1920 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
1921 return err;
1922}
1923
0e6ea2a4
IS
1924static void mlxsw_sp_nexthop4_fini(struct mlxsw_sp *mlxsw_sp,
1925 struct mlxsw_sp_nexthop *nh)
a8c97014
IS
1926{
1927 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
9665b745 1928 mlxsw_sp_nexthop_rif_fini(nh);
c53b8e1b 1929 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
a7ff87ac
JP
1930}
1931
0e6ea2a4
IS
1932static void mlxsw_sp_nexthop4_event(struct mlxsw_sp *mlxsw_sp,
1933 unsigned long event, struct fib_nh *fib_nh)
ad178c8e
IS
1934{
1935 struct mlxsw_sp_nexthop_key key;
1936 struct mlxsw_sp_nexthop *nh;
bf95233e 1937 struct mlxsw_sp_rif *rif;
ad178c8e 1938
9011b677 1939 if (mlxsw_sp->router->aborted)
ad178c8e
IS
1940 return;
1941
1942 key.fib_nh = fib_nh;
1943 nh = mlxsw_sp_nexthop_lookup(mlxsw_sp, key);
1944 if (WARN_ON_ONCE(!nh))
1945 return;
1946
bf95233e
AS
1947 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, fib_nh->nh_dev);
1948 if (!rif)
ad178c8e
IS
1949 return;
1950
1951 switch (event) {
1952 case FIB_EVENT_NH_ADD:
bf95233e 1953 mlxsw_sp_nexthop_rif_init(nh, rif);
ad178c8e
IS
1954 mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
1955 break;
1956 case FIB_EVENT_NH_DEL:
1957 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
9665b745 1958 mlxsw_sp_nexthop_rif_fini(nh);
ad178c8e
IS
1959 break;
1960 }
1961
1962 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
1963}
1964
9665b745 1965static void mlxsw_sp_nexthop_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
bf95233e 1966 struct mlxsw_sp_rif *rif)
9665b745
IS
1967{
1968 struct mlxsw_sp_nexthop *nh, *tmp;
1969
bf95233e 1970 list_for_each_entry_safe(nh, tmp, &rif->nexthop_list, rif_list_node) {
9665b745
IS
1971 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
1972 mlxsw_sp_nexthop_rif_fini(nh);
1973 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
1974 }
1975}
1976
a7ff87ac 1977static struct mlxsw_sp_nexthop_group *
0e6ea2a4 1978mlxsw_sp_nexthop4_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi)
a7ff87ac
JP
1979{
1980 struct mlxsw_sp_nexthop_group *nh_grp;
1981 struct mlxsw_sp_nexthop *nh;
1982 struct fib_nh *fib_nh;
1983 size_t alloc_size;
1984 int i;
1985 int err;
1986
1987 alloc_size = sizeof(*nh_grp) +
1988 fi->fib_nhs * sizeof(struct mlxsw_sp_nexthop);
1989 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
1990 if (!nh_grp)
1991 return ERR_PTR(-ENOMEM);
1992 INIT_LIST_HEAD(&nh_grp->fib_list);
58adf2c4
IS
1993 nh_grp->neigh_tbl = &arp_tbl;
1994
b3e8d1eb 1995 nh_grp->gateway = fi->fib_nh->nh_scope == RT_SCOPE_LINK;
a7ff87ac 1996 nh_grp->count = fi->fib_nhs;
e9ad5e7d 1997 nh_grp->key.fi = fi;
7387dbbc 1998 fib_info_hold(fi);
a7ff87ac
JP
1999 for (i = 0; i < nh_grp->count; i++) {
2000 nh = &nh_grp->nexthops[i];
2001 fib_nh = &fi->fib_nh[i];
0e6ea2a4 2002 err = mlxsw_sp_nexthop4_init(mlxsw_sp, nh_grp, nh, fib_nh);
a7ff87ac 2003 if (err)
0e6ea2a4 2004 goto err_nexthop4_init;
a7ff87ac 2005 }
e9ad5e7d
IS
2006 err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
2007 if (err)
2008 goto err_nexthop_group_insert;
a7ff87ac
JP
2009 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
2010 return nh_grp;
2011
e9ad5e7d 2012err_nexthop_group_insert:
0e6ea2a4 2013err_nexthop4_init:
df6dd79b
IS
2014 for (i--; i >= 0; i--) {
2015 nh = &nh_grp->nexthops[i];
0e6ea2a4 2016 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
df6dd79b 2017 }
7387dbbc 2018 fib_info_put(nh_grp->key.fi);
a7ff87ac
JP
2019 kfree(nh_grp);
2020 return ERR_PTR(err);
2021}
2022
2023static void
0e6ea2a4
IS
2024mlxsw_sp_nexthop4_group_destroy(struct mlxsw_sp *mlxsw_sp,
2025 struct mlxsw_sp_nexthop_group *nh_grp)
a7ff87ac
JP
2026{
2027 struct mlxsw_sp_nexthop *nh;
2028 int i;
2029
e9ad5e7d 2030 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
a7ff87ac
JP
2031 for (i = 0; i < nh_grp->count; i++) {
2032 nh = &nh_grp->nexthops[i];
0e6ea2a4 2033 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
a7ff87ac 2034 }
58312125
IS
2035 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
2036 WARN_ON_ONCE(nh_grp->adj_index_valid);
7387dbbc 2037 fib_info_put(nh_grp->key.fi);
a7ff87ac
JP
2038 kfree(nh_grp);
2039}
2040
0e6ea2a4
IS
2041static int mlxsw_sp_nexthop4_group_get(struct mlxsw_sp *mlxsw_sp,
2042 struct mlxsw_sp_fib_entry *fib_entry,
2043 struct fib_info *fi)
a7ff87ac 2044{
e9ad5e7d 2045 struct mlxsw_sp_nexthop_group_key key;
a7ff87ac
JP
2046 struct mlxsw_sp_nexthop_group *nh_grp;
2047
e9ad5e7d
IS
2048 key.fi = fi;
2049 nh_grp = mlxsw_sp_nexthop_group_lookup(mlxsw_sp, key);
a7ff87ac 2050 if (!nh_grp) {
0e6ea2a4 2051 nh_grp = mlxsw_sp_nexthop4_group_create(mlxsw_sp, fi);
a7ff87ac
JP
2052 if (IS_ERR(nh_grp))
2053 return PTR_ERR(nh_grp);
2054 }
2055 list_add_tail(&fib_entry->nexthop_group_node, &nh_grp->fib_list);
2056 fib_entry->nh_group = nh_grp;
2057 return 0;
2058}
2059
0e6ea2a4
IS
2060static void mlxsw_sp_nexthop4_group_put(struct mlxsw_sp *mlxsw_sp,
2061 struct mlxsw_sp_fib_entry *fib_entry)
a7ff87ac
JP
2062{
2063 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2064
2065 list_del(&fib_entry->nexthop_group_node);
2066 if (!list_empty(&nh_grp->fib_list))
2067 return;
0e6ea2a4 2068 mlxsw_sp_nexthop4_group_destroy(mlxsw_sp, nh_grp);
a7ff87ac
JP
2069}
2070
4f1c7f1f
IS
2071static bool
2072mlxsw_sp_fib4_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
2073{
2074 struct mlxsw_sp_fib4_entry *fib4_entry;
2075
2076 fib4_entry = container_of(fib_entry, struct mlxsw_sp_fib4_entry,
2077 common);
2078 return !fib4_entry->tos;
2079}
2080
013b20f9
IS
2081static bool
2082mlxsw_sp_fib_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
2083{
2084 struct mlxsw_sp_nexthop_group *nh_group = fib_entry->nh_group;
2085
4f1c7f1f
IS
2086 switch (fib_entry->fib_node->fib->proto) {
2087 case MLXSW_SP_L3_PROTO_IPV4:
2088 if (!mlxsw_sp_fib4_entry_should_offload(fib_entry))
2089 return false;
2090 break;
2091 case MLXSW_SP_L3_PROTO_IPV6:
2092 break;
2093 }
9aecce1c 2094
013b20f9
IS
2095 switch (fib_entry->type) {
2096 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
2097 return !!nh_group->adj_index_valid;
2098 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
70ad3506 2099 return !!nh_group->nh_rif;
013b20f9
IS
2100 default:
2101 return false;
2102 }
2103}
2104
3984d1a8
IS
2105static void
2106mlxsw_sp_fib4_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
2107{
2108 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2109 int i;
2110
2111 if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) {
2112 nh_grp->nexthops->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
2113 return;
2114 }
2115
2116 for (i = 0; i < nh_grp->count; i++) {
2117 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2118
2119 if (nh->offloaded)
2120 nh->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
2121 else
2122 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
2123 }
2124}
2125
2126static void
2127mlxsw_sp_fib4_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
2128{
2129 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2130 int i;
2131
2132 for (i = 0; i < nh_grp->count; i++) {
2133 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2134
2135 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
2136 }
2137}
2138
013b20f9
IS
2139static void mlxsw_sp_fib_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
2140{
76610ebb 2141 switch (fib_entry->fib_node->fib->proto) {
013b20f9 2142 case MLXSW_SP_L3_PROTO_IPV4:
3984d1a8 2143 mlxsw_sp_fib4_entry_offload_set(fib_entry);
013b20f9
IS
2144 break;
2145 case MLXSW_SP_L3_PROTO_IPV6:
2146 WARN_ON_ONCE(1);
2147 }
2148}
2149
2150static void
2151mlxsw_sp_fib_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
2152{
76610ebb 2153 switch (fib_entry->fib_node->fib->proto) {
013b20f9 2154 case MLXSW_SP_L3_PROTO_IPV4:
3984d1a8 2155 mlxsw_sp_fib4_entry_offload_unset(fib_entry);
013b20f9
IS
2156 break;
2157 case MLXSW_SP_L3_PROTO_IPV6:
2158 WARN_ON_ONCE(1);
2159 }
013b20f9
IS
2160}
2161
2162static void
2163mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
2164 enum mlxsw_reg_ralue_op op, int err)
2165{
2166 switch (op) {
2167 case MLXSW_REG_RALUE_OP_WRITE_DELETE:
013b20f9
IS
2168 return mlxsw_sp_fib_entry_offload_unset(fib_entry);
2169 case MLXSW_REG_RALUE_OP_WRITE_WRITE:
2170 if (err)
2171 return;
1353ee70 2172 if (mlxsw_sp_fib_entry_should_offload(fib_entry))
013b20f9 2173 mlxsw_sp_fib_entry_offload_set(fib_entry);
1353ee70 2174 else if (!mlxsw_sp_fib_entry_should_offload(fib_entry))
013b20f9
IS
2175 mlxsw_sp_fib_entry_offload_unset(fib_entry);
2176 return;
2177 default:
2178 return;
2179 }
2180}
2181
9dbf4d76
IS
2182static void
2183mlxsw_sp_fib_entry_ralue_pack(char *ralue_pl,
2184 const struct mlxsw_sp_fib_entry *fib_entry,
2185 enum mlxsw_reg_ralue_op op)
a7ff87ac 2186{
76610ebb 2187 struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib;
9dbf4d76
IS
2188 enum mlxsw_reg_ralxx_protocol proto;
2189 u32 *p_dip;
2190
2191 proto = (enum mlxsw_reg_ralxx_protocol) fib->proto;
2192
2193 switch (fib->proto) {
2194 case MLXSW_SP_L3_PROTO_IPV4:
2195 p_dip = (u32 *) fib_entry->fib_node->key.addr;
2196 mlxsw_reg_ralue_pack4(ralue_pl, proto, op, fib->vr->id,
2197 fib_entry->fib_node->key.prefix_len,
2198 *p_dip);
2199 break;
2200 case MLXSW_SP_L3_PROTO_IPV6:
2201 mlxsw_reg_ralue_pack6(ralue_pl, proto, op, fib->vr->id,
2202 fib_entry->fib_node->key.prefix_len,
2203 fib_entry->fib_node->key.addr);
2204 break;
2205 }
2206}
2207
2208static int mlxsw_sp_fib_entry_op_remote(struct mlxsw_sp *mlxsw_sp,
2209 struct mlxsw_sp_fib_entry *fib_entry,
2210 enum mlxsw_reg_ralue_op op)
2211{
2212 char ralue_pl[MLXSW_REG_RALUE_LEN];
a7ff87ac
JP
2213 enum mlxsw_reg_ralue_trap_action trap_action;
2214 u16 trap_id = 0;
2215 u32 adjacency_index = 0;
2216 u16 ecmp_size = 0;
2217
2218 /* In case the nexthop group adjacency index is valid, use it
2219 * with provided ECMP size. Otherwise, setup trap and pass
2220 * traffic to kernel.
2221 */
4b411477 2222 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
a7ff87ac
JP
2223 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
2224 adjacency_index = fib_entry->nh_group->adj_index;
2225 ecmp_size = fib_entry->nh_group->ecmp_size;
2226 } else {
2227 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
2228 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
2229 }
2230
9dbf4d76 2231 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
a7ff87ac
JP
2232 mlxsw_reg_ralue_act_remote_pack(ralue_pl, trap_action, trap_id,
2233 adjacency_index, ecmp_size);
2234 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2235}
2236
9dbf4d76
IS
2237static int mlxsw_sp_fib_entry_op_local(struct mlxsw_sp *mlxsw_sp,
2238 struct mlxsw_sp_fib_entry *fib_entry,
2239 enum mlxsw_reg_ralue_op op)
61c503f9 2240{
bf95233e 2241 struct mlxsw_sp_rif *rif = fib_entry->nh_group->nh_rif;
70ad3506 2242 enum mlxsw_reg_ralue_trap_action trap_action;
61c503f9 2243 char ralue_pl[MLXSW_REG_RALUE_LEN];
70ad3506 2244 u16 trap_id = 0;
bf95233e 2245 u16 rif_index = 0;
70ad3506
IS
2246
2247 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
2248 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
bf95233e 2249 rif_index = rif->rif_index;
70ad3506
IS
2250 } else {
2251 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
2252 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
2253 }
61c503f9 2254
9dbf4d76 2255 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
bf95233e
AS
2256 mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, trap_id,
2257 rif_index);
61c503f9
JP
2258 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2259}
2260
9dbf4d76
IS
2261static int mlxsw_sp_fib_entry_op_trap(struct mlxsw_sp *mlxsw_sp,
2262 struct mlxsw_sp_fib_entry *fib_entry,
2263 enum mlxsw_reg_ralue_op op)
61c503f9
JP
2264{
2265 char ralue_pl[MLXSW_REG_RALUE_LEN];
61c503f9 2266
9dbf4d76 2267 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
61c503f9
JP
2268 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
2269 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
2270}
2271
9dbf4d76
IS
2272static int __mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
2273 struct mlxsw_sp_fib_entry *fib_entry,
2274 enum mlxsw_reg_ralue_op op)
61c503f9
JP
2275{
2276 switch (fib_entry->type) {
2277 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
9dbf4d76 2278 return mlxsw_sp_fib_entry_op_remote(mlxsw_sp, fib_entry, op);
61c503f9 2279 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
9dbf4d76 2280 return mlxsw_sp_fib_entry_op_local(mlxsw_sp, fib_entry, op);
61c503f9 2281 case MLXSW_SP_FIB_ENTRY_TYPE_TRAP:
9dbf4d76 2282 return mlxsw_sp_fib_entry_op_trap(mlxsw_sp, fib_entry, op);
61c503f9
JP
2283 }
2284 return -EINVAL;
2285}
2286
2287static int mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
2288 struct mlxsw_sp_fib_entry *fib_entry,
2289 enum mlxsw_reg_ralue_op op)
2290{
9dbf4d76 2291 int err = __mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry, op);
013b20f9 2292
013b20f9 2293 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, err);
9dbf4d76 2294
013b20f9 2295 return err;
61c503f9
JP
2296}
2297
2298static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
2299 struct mlxsw_sp_fib_entry *fib_entry)
2300{
7146da31
JP
2301 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
2302 MLXSW_REG_RALUE_OP_WRITE_WRITE);
61c503f9
JP
2303}
2304
2305static int mlxsw_sp_fib_entry_del(struct mlxsw_sp *mlxsw_sp,
2306 struct mlxsw_sp_fib_entry *fib_entry)
2307{
2308 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
2309 MLXSW_REG_RALUE_OP_WRITE_DELETE);
2310}
2311
61c503f9 2312static int
013b20f9
IS
2313mlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp,
2314 const struct fib_entry_notifier_info *fen_info,
2315 struct mlxsw_sp_fib_entry *fib_entry)
61c503f9 2316{
b45f64d1 2317 struct fib_info *fi = fen_info->fi;
61c503f9 2318
97989ee0
IS
2319 switch (fen_info->type) {
2320 case RTN_BROADCAST: /* fall through */
2321 case RTN_LOCAL:
61c503f9
JP
2322 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
2323 return 0;
97989ee0
IS
2324 case RTN_UNREACHABLE: /* fall through */
2325 case RTN_BLACKHOLE: /* fall through */
2326 case RTN_PROHIBIT:
2327 /* Packets hitting these routes need to be trapped, but
2328 * can do so with a lower priority than packets directed
2329 * at the host, so use action type local instead of trap.
2330 */
61c503f9 2331 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
97989ee0
IS
2332 return 0;
2333 case RTN_UNICAST:
2334 if (fi->fib_nh->nh_scope != RT_SCOPE_LINK)
2335 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
2336 else
2337 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
2338 return 0;
2339 default:
2340 return -EINVAL;
2341 }
a7ff87ac
JP
2342}
2343
4f1c7f1f 2344static struct mlxsw_sp_fib4_entry *
9aecce1c
IS
2345mlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp,
2346 struct mlxsw_sp_fib_node *fib_node,
2347 const struct fib_entry_notifier_info *fen_info)
61c503f9 2348{
4f1c7f1f 2349 struct mlxsw_sp_fib4_entry *fib4_entry;
61c503f9 2350 struct mlxsw_sp_fib_entry *fib_entry;
61c503f9
JP
2351 int err;
2352
4f1c7f1f
IS
2353 fib4_entry = kzalloc(sizeof(*fib4_entry), GFP_KERNEL);
2354 if (!fib4_entry)
2355 return ERR_PTR(-ENOMEM);
2356 fib_entry = &fib4_entry->common;
61c503f9 2357
013b20f9 2358 err = mlxsw_sp_fib4_entry_type_set(mlxsw_sp, fen_info, fib_entry);
61c503f9 2359 if (err)
013b20f9 2360 goto err_fib4_entry_type_set;
61c503f9 2361
0e6ea2a4 2362 err = mlxsw_sp_nexthop4_group_get(mlxsw_sp, fib_entry, fen_info->fi);
b8399a1e 2363 if (err)
0e6ea2a4 2364 goto err_nexthop4_group_get;
b8399a1e 2365
4f1c7f1f
IS
2366 fib4_entry->prio = fen_info->fi->fib_priority;
2367 fib4_entry->tb_id = fen_info->tb_id;
2368 fib4_entry->type = fen_info->type;
2369 fib4_entry->tos = fen_info->tos;
9aecce1c
IS
2370
2371 fib_entry->fib_node = fib_node;
2372
4f1c7f1f 2373 return fib4_entry;
5b004412 2374
0e6ea2a4 2375err_nexthop4_group_get:
013b20f9 2376err_fib4_entry_type_set:
4f1c7f1f 2377 kfree(fib4_entry);
5b004412
JP
2378 return ERR_PTR(err);
2379}
2380
9aecce1c 2381static void mlxsw_sp_fib4_entry_destroy(struct mlxsw_sp *mlxsw_sp,
4f1c7f1f 2382 struct mlxsw_sp_fib4_entry *fib4_entry)
9aecce1c 2383{
0e6ea2a4 2384 mlxsw_sp_nexthop4_group_put(mlxsw_sp, &fib4_entry->common);
4f1c7f1f 2385 kfree(fib4_entry);
9aecce1c
IS
2386}
2387
2388static struct mlxsw_sp_fib_node *
160e22aa
IS
2389mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
2390 size_t addr_len, unsigned char prefix_len);
9aecce1c 2391
4f1c7f1f 2392static struct mlxsw_sp_fib4_entry *
9aecce1c
IS
2393mlxsw_sp_fib4_entry_lookup(struct mlxsw_sp *mlxsw_sp,
2394 const struct fib_entry_notifier_info *fen_info)
5b004412 2395{
4f1c7f1f 2396 struct mlxsw_sp_fib4_entry *fib4_entry;
9aecce1c 2397 struct mlxsw_sp_fib_node *fib_node;
160e22aa
IS
2398 struct mlxsw_sp_fib *fib;
2399 struct mlxsw_sp_vr *vr;
2400
2401 vr = mlxsw_sp_vr_find(mlxsw_sp, fen_info->tb_id);
2402 if (!vr)
2403 return NULL;
2404 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV4);
5b004412 2405
160e22aa
IS
2406 fib_node = mlxsw_sp_fib_node_lookup(fib, &fen_info->dst,
2407 sizeof(fen_info->dst),
2408 fen_info->dst_len);
2409 if (!fib_node)
9aecce1c
IS
2410 return NULL;
2411
4f1c7f1f
IS
2412 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
2413 if (fib4_entry->tb_id == fen_info->tb_id &&
2414 fib4_entry->tos == fen_info->tos &&
2415 fib4_entry->type == fen_info->type &&
2416 fib4_entry->common.nh_group->key.fi == fen_info->fi) {
2417 return fib4_entry;
9aecce1c
IS
2418 }
2419 }
2420
2421 return NULL;
2422}
2423
2424static const struct rhashtable_params mlxsw_sp_fib_ht_params = {
2425 .key_offset = offsetof(struct mlxsw_sp_fib_node, key),
2426 .head_offset = offsetof(struct mlxsw_sp_fib_node, ht_node),
2427 .key_len = sizeof(struct mlxsw_sp_fib_key),
2428 .automatic_shrinking = true,
2429};
2430
2431static int mlxsw_sp_fib_node_insert(struct mlxsw_sp_fib *fib,
2432 struct mlxsw_sp_fib_node *fib_node)
2433{
2434 return rhashtable_insert_fast(&fib->ht, &fib_node->ht_node,
2435 mlxsw_sp_fib_ht_params);
2436}
2437
2438static void mlxsw_sp_fib_node_remove(struct mlxsw_sp_fib *fib,
2439 struct mlxsw_sp_fib_node *fib_node)
2440{
2441 rhashtable_remove_fast(&fib->ht, &fib_node->ht_node,
2442 mlxsw_sp_fib_ht_params);
2443}
2444
2445static struct mlxsw_sp_fib_node *
2446mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
2447 size_t addr_len, unsigned char prefix_len)
2448{
2449 struct mlxsw_sp_fib_key key;
2450
2451 memset(&key, 0, sizeof(key));
2452 memcpy(key.addr, addr, addr_len);
2453 key.prefix_len = prefix_len;
2454 return rhashtable_lookup_fast(&fib->ht, &key, mlxsw_sp_fib_ht_params);
2455}
2456
2457static struct mlxsw_sp_fib_node *
76610ebb 2458mlxsw_sp_fib_node_create(struct mlxsw_sp_fib *fib, const void *addr,
9aecce1c
IS
2459 size_t addr_len, unsigned char prefix_len)
2460{
2461 struct mlxsw_sp_fib_node *fib_node;
2462
2463 fib_node = kzalloc(sizeof(*fib_node), GFP_KERNEL);
2464 if (!fib_node)
5b004412
JP
2465 return NULL;
2466
9aecce1c 2467 INIT_LIST_HEAD(&fib_node->entry_list);
76610ebb 2468 list_add(&fib_node->list, &fib->node_list);
9aecce1c
IS
2469 memcpy(fib_node->key.addr, addr, addr_len);
2470 fib_node->key.prefix_len = prefix_len;
9aecce1c
IS
2471
2472 return fib_node;
2473}
2474
2475static void mlxsw_sp_fib_node_destroy(struct mlxsw_sp_fib_node *fib_node)
2476{
9aecce1c
IS
2477 list_del(&fib_node->list);
2478 WARN_ON(!list_empty(&fib_node->entry_list));
2479 kfree(fib_node);
2480}
2481
2482static bool
2483mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
2484 const struct mlxsw_sp_fib_entry *fib_entry)
2485{
2486 return list_first_entry(&fib_node->entry_list,
2487 struct mlxsw_sp_fib_entry, list) == fib_entry;
2488}
2489
2490static void mlxsw_sp_fib_node_prefix_inc(struct mlxsw_sp_fib_node *fib_node)
2491{
2492 unsigned char prefix_len = fib_node->key.prefix_len;
76610ebb 2493 struct mlxsw_sp_fib *fib = fib_node->fib;
9aecce1c
IS
2494
2495 if (fib->prefix_ref_count[prefix_len]++ == 0)
2496 mlxsw_sp_prefix_usage_set(&fib->prefix_usage, prefix_len);
2497}
2498
2499static void mlxsw_sp_fib_node_prefix_dec(struct mlxsw_sp_fib_node *fib_node)
2500{
2501 unsigned char prefix_len = fib_node->key.prefix_len;
76610ebb 2502 struct mlxsw_sp_fib *fib = fib_node->fib;
9aecce1c
IS
2503
2504 if (--fib->prefix_ref_count[prefix_len] == 0)
2505 mlxsw_sp_prefix_usage_clear(&fib->prefix_usage, prefix_len);
5b004412
JP
2506}
2507
76610ebb
IS
2508static int mlxsw_sp_fib_node_init(struct mlxsw_sp *mlxsw_sp,
2509 struct mlxsw_sp_fib_node *fib_node,
2510 struct mlxsw_sp_fib *fib)
2511{
2512 struct mlxsw_sp_prefix_usage req_prefix_usage;
2513 struct mlxsw_sp_lpm_tree *lpm_tree;
2514 int err;
2515
2516 err = mlxsw_sp_fib_node_insert(fib, fib_node);
2517 if (err)
2518 return err;
2519 fib_node->fib = fib;
2520
2521 mlxsw_sp_prefix_usage_cpy(&req_prefix_usage, &fib->prefix_usage);
2522 mlxsw_sp_prefix_usage_set(&req_prefix_usage, fib_node->key.prefix_len);
2523
2524 if (!mlxsw_sp_prefix_usage_none(&fib->prefix_usage)) {
2525 err = mlxsw_sp_vr_lpm_tree_check(mlxsw_sp, fib,
2526 &req_prefix_usage);
2527 if (err)
2528 goto err_tree_check;
2529 } else {
2530 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
2531 fib->proto);
2532 if (IS_ERR(lpm_tree))
2533 return PTR_ERR(lpm_tree);
2534 fib->lpm_tree = lpm_tree;
2535 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib);
2536 if (err)
2537 goto err_tree_bind;
2538 }
2539
2540 mlxsw_sp_fib_node_prefix_inc(fib_node);
2541
2542 return 0;
2543
2544err_tree_bind:
2545 fib->lpm_tree = NULL;
2546 mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree);
2547err_tree_check:
2548 fib_node->fib = NULL;
2549 mlxsw_sp_fib_node_remove(fib, fib_node);
2550 return err;
2551}
2552
2553static void mlxsw_sp_fib_node_fini(struct mlxsw_sp *mlxsw_sp,
2554 struct mlxsw_sp_fib_node *fib_node)
2555{
2556 struct mlxsw_sp_lpm_tree *lpm_tree = fib_node->fib->lpm_tree;
2557 struct mlxsw_sp_fib *fib = fib_node->fib;
2558
2559 mlxsw_sp_fib_node_prefix_dec(fib_node);
2560
2561 if (mlxsw_sp_prefix_usage_none(&fib->prefix_usage)) {
2562 mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, fib);
2563 fib->lpm_tree = NULL;
2564 mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree);
2565 } else {
2566 mlxsw_sp_vr_lpm_tree_check(mlxsw_sp, fib, &fib->prefix_usage);
2567 }
2568
2569 fib_node->fib = NULL;
2570 mlxsw_sp_fib_node_remove(fib, fib_node);
2571}
2572
9aecce1c 2573static struct mlxsw_sp_fib_node *
731ea1ca
IS
2574mlxsw_sp_fib_node_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id, const void *addr,
2575 size_t addr_len, unsigned char prefix_len,
2576 enum mlxsw_sp_l3proto proto)
5b004412 2577{
9aecce1c 2578 struct mlxsw_sp_fib_node *fib_node;
76610ebb 2579 struct mlxsw_sp_fib *fib;
9aecce1c
IS
2580 struct mlxsw_sp_vr *vr;
2581 int err;
2582
731ea1ca 2583 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id);
9aecce1c
IS
2584 if (IS_ERR(vr))
2585 return ERR_CAST(vr);
731ea1ca 2586 fib = mlxsw_sp_vr_fib(vr, proto);
9aecce1c 2587
731ea1ca 2588 fib_node = mlxsw_sp_fib_node_lookup(fib, addr, addr_len, prefix_len);
9aecce1c
IS
2589 if (fib_node)
2590 return fib_node;
5b004412 2591
731ea1ca 2592 fib_node = mlxsw_sp_fib_node_create(fib, addr, addr_len, prefix_len);
9aecce1c
IS
2593 if (!fib_node) {
2594 err = -ENOMEM;
2595 goto err_fib_node_create;
5b004412 2596 }
9aecce1c 2597
76610ebb
IS
2598 err = mlxsw_sp_fib_node_init(mlxsw_sp, fib_node, fib);
2599 if (err)
2600 goto err_fib_node_init;
2601
9aecce1c
IS
2602 return fib_node;
2603
76610ebb
IS
2604err_fib_node_init:
2605 mlxsw_sp_fib_node_destroy(fib_node);
9aecce1c 2606err_fib_node_create:
76610ebb 2607 mlxsw_sp_vr_put(vr);
9aecce1c 2608 return ERR_PTR(err);
5b004412
JP
2609}
2610
731ea1ca
IS
2611static void mlxsw_sp_fib_node_put(struct mlxsw_sp *mlxsw_sp,
2612 struct mlxsw_sp_fib_node *fib_node)
5b004412 2613{
76610ebb 2614 struct mlxsw_sp_vr *vr = fib_node->fib->vr;
5b004412 2615
9aecce1c
IS
2616 if (!list_empty(&fib_node->entry_list))
2617 return;
76610ebb 2618 mlxsw_sp_fib_node_fini(mlxsw_sp, fib_node);
9aecce1c 2619 mlxsw_sp_fib_node_destroy(fib_node);
76610ebb 2620 mlxsw_sp_vr_put(vr);
61c503f9
JP
2621}
2622
4f1c7f1f 2623static struct mlxsw_sp_fib4_entry *
9aecce1c 2624mlxsw_sp_fib4_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
4f1c7f1f 2625 const struct mlxsw_sp_fib4_entry *new4_entry)
61c503f9 2626{
4f1c7f1f 2627 struct mlxsw_sp_fib4_entry *fib4_entry;
9aecce1c 2628
4f1c7f1f
IS
2629 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
2630 if (fib4_entry->tb_id > new4_entry->tb_id)
9aecce1c 2631 continue;
4f1c7f1f 2632 if (fib4_entry->tb_id != new4_entry->tb_id)
9aecce1c 2633 break;
4f1c7f1f 2634 if (fib4_entry->tos > new4_entry->tos)
9aecce1c 2635 continue;
4f1c7f1f
IS
2636 if (fib4_entry->prio >= new4_entry->prio ||
2637 fib4_entry->tos < new4_entry->tos)
2638 return fib4_entry;
9aecce1c
IS
2639 }
2640
2641 return NULL;
2642}
2643
4f1c7f1f
IS
2644static int
2645mlxsw_sp_fib4_node_list_append(struct mlxsw_sp_fib4_entry *fib4_entry,
2646 struct mlxsw_sp_fib4_entry *new4_entry)
4283bce5
IS
2647{
2648 struct mlxsw_sp_fib_node *fib_node;
2649
4f1c7f1f 2650 if (WARN_ON(!fib4_entry))
4283bce5
IS
2651 return -EINVAL;
2652
4f1c7f1f
IS
2653 fib_node = fib4_entry->common.fib_node;
2654 list_for_each_entry_from(fib4_entry, &fib_node->entry_list,
2655 common.list) {
2656 if (fib4_entry->tb_id != new4_entry->tb_id ||
2657 fib4_entry->tos != new4_entry->tos ||
2658 fib4_entry->prio != new4_entry->prio)
4283bce5
IS
2659 break;
2660 }
2661
4f1c7f1f 2662 list_add_tail(&new4_entry->common.list, &fib4_entry->common.list);
4283bce5
IS
2663 return 0;
2664}
2665
9aecce1c 2666static int
9efbee6f 2667mlxsw_sp_fib4_node_list_insert(struct mlxsw_sp_fib4_entry *new4_entry,
599cf8f9 2668 bool replace, bool append)
9aecce1c 2669{
9efbee6f 2670 struct mlxsw_sp_fib_node *fib_node = new4_entry->common.fib_node;
4f1c7f1f 2671 struct mlxsw_sp_fib4_entry *fib4_entry;
9aecce1c 2672
4f1c7f1f 2673 fib4_entry = mlxsw_sp_fib4_node_entry_find(fib_node, new4_entry);
9aecce1c 2674
4283bce5 2675 if (append)
4f1c7f1f
IS
2676 return mlxsw_sp_fib4_node_list_append(fib4_entry, new4_entry);
2677 if (replace && WARN_ON(!fib4_entry))
599cf8f9 2678 return -EINVAL;
4283bce5 2679
599cf8f9
IS
2680 /* Insert new entry before replaced one, so that we can later
2681 * remove the second.
2682 */
4f1c7f1f
IS
2683 if (fib4_entry) {
2684 list_add_tail(&new4_entry->common.list,
2685 &fib4_entry->common.list);
9aecce1c 2686 } else {
4f1c7f1f 2687 struct mlxsw_sp_fib4_entry *last;
9aecce1c 2688
4f1c7f1f
IS
2689 list_for_each_entry(last, &fib_node->entry_list, common.list) {
2690 if (new4_entry->tb_id > last->tb_id)
9aecce1c 2691 break;
4f1c7f1f 2692 fib4_entry = last;
9aecce1c
IS
2693 }
2694
4f1c7f1f
IS
2695 if (fib4_entry)
2696 list_add(&new4_entry->common.list,
2697 &fib4_entry->common.list);
9aecce1c 2698 else
4f1c7f1f
IS
2699 list_add(&new4_entry->common.list,
2700 &fib_node->entry_list);
9aecce1c
IS
2701 }
2702
2703 return 0;
2704}
2705
2706static void
4f1c7f1f 2707mlxsw_sp_fib4_node_list_remove(struct mlxsw_sp_fib4_entry *fib4_entry)
9aecce1c 2708{
4f1c7f1f 2709 list_del(&fib4_entry->common.list);
9aecce1c
IS
2710}
2711
80c238f9
IS
2712static int mlxsw_sp_fib_node_entry_add(struct mlxsw_sp *mlxsw_sp,
2713 struct mlxsw_sp_fib_entry *fib_entry)
9aecce1c 2714{
9efbee6f
IS
2715 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
2716
9aecce1c
IS
2717 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
2718 return 0;
2719
2720 /* To prevent packet loss, overwrite the previously offloaded
2721 * entry.
2722 */
2723 if (!list_is_singular(&fib_node->entry_list)) {
2724 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
2725 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
2726
2727 mlxsw_sp_fib_entry_offload_refresh(n, op, 0);
2728 }
2729
2730 return mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
2731}
2732
80c238f9
IS
2733static void mlxsw_sp_fib_node_entry_del(struct mlxsw_sp *mlxsw_sp,
2734 struct mlxsw_sp_fib_entry *fib_entry)
9aecce1c 2735{
9efbee6f
IS
2736 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
2737
9aecce1c
IS
2738 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
2739 return;
2740
2741 /* Promote the next entry by overwriting the deleted entry */
2742 if (!list_is_singular(&fib_node->entry_list)) {
2743 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
2744 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
2745
2746 mlxsw_sp_fib_entry_update(mlxsw_sp, n);
2747 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
2748 return;
2749 }
2750
2751 mlxsw_sp_fib_entry_del(mlxsw_sp, fib_entry);
2752}
2753
2754static int mlxsw_sp_fib4_node_entry_link(struct mlxsw_sp *mlxsw_sp,
4f1c7f1f 2755 struct mlxsw_sp_fib4_entry *fib4_entry,
599cf8f9 2756 bool replace, bool append)
9aecce1c 2757{
9aecce1c
IS
2758 int err;
2759
9efbee6f 2760 err = mlxsw_sp_fib4_node_list_insert(fib4_entry, replace, append);
9aecce1c
IS
2761 if (err)
2762 return err;
2763
80c238f9 2764 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib4_entry->common);
9aecce1c 2765 if (err)
80c238f9 2766 goto err_fib_node_entry_add;
9aecce1c 2767
9aecce1c
IS
2768 return 0;
2769
80c238f9 2770err_fib_node_entry_add:
4f1c7f1f 2771 mlxsw_sp_fib4_node_list_remove(fib4_entry);
9aecce1c
IS
2772 return err;
2773}
2774
2775static void
2776mlxsw_sp_fib4_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
4f1c7f1f 2777 struct mlxsw_sp_fib4_entry *fib4_entry)
9aecce1c 2778{
80c238f9 2779 mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib4_entry->common);
4f1c7f1f 2780 mlxsw_sp_fib4_node_list_remove(fib4_entry);
9aecce1c
IS
2781}
2782
599cf8f9 2783static void mlxsw_sp_fib4_entry_replace(struct mlxsw_sp *mlxsw_sp,
4f1c7f1f 2784 struct mlxsw_sp_fib4_entry *fib4_entry,
599cf8f9
IS
2785 bool replace)
2786{
4f1c7f1f
IS
2787 struct mlxsw_sp_fib_node *fib_node = fib4_entry->common.fib_node;
2788 struct mlxsw_sp_fib4_entry *replaced;
599cf8f9
IS
2789
2790 if (!replace)
2791 return;
2792
2793 /* We inserted the new entry before replaced one */
4f1c7f1f 2794 replaced = list_next_entry(fib4_entry, common.list);
599cf8f9
IS
2795
2796 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, replaced);
2797 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, replaced);
731ea1ca 2798 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
599cf8f9
IS
2799}
2800
9aecce1c
IS
2801static int
2802mlxsw_sp_router_fib4_add(struct mlxsw_sp *mlxsw_sp,
4283bce5 2803 const struct fib_entry_notifier_info *fen_info,
599cf8f9 2804 bool replace, bool append)
9aecce1c 2805{
4f1c7f1f 2806 struct mlxsw_sp_fib4_entry *fib4_entry;
9aecce1c 2807 struct mlxsw_sp_fib_node *fib_node;
61c503f9
JP
2808 int err;
2809
9011b677 2810 if (mlxsw_sp->router->aborted)
b45f64d1
JP
2811 return 0;
2812
731ea1ca
IS
2813 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, fen_info->tb_id,
2814 &fen_info->dst, sizeof(fen_info->dst),
2815 fen_info->dst_len,
2816 MLXSW_SP_L3_PROTO_IPV4);
9aecce1c
IS
2817 if (IS_ERR(fib_node)) {
2818 dev_warn(mlxsw_sp->bus_info->dev, "Failed to get FIB node\n");
2819 return PTR_ERR(fib_node);
b45f64d1 2820 }
61c503f9 2821
4f1c7f1f
IS
2822 fib4_entry = mlxsw_sp_fib4_entry_create(mlxsw_sp, fib_node, fen_info);
2823 if (IS_ERR(fib4_entry)) {
9aecce1c 2824 dev_warn(mlxsw_sp->bus_info->dev, "Failed to create FIB entry\n");
4f1c7f1f 2825 err = PTR_ERR(fib4_entry);
9aecce1c
IS
2826 goto err_fib4_entry_create;
2827 }
5b004412 2828
4f1c7f1f 2829 err = mlxsw_sp_fib4_node_entry_link(mlxsw_sp, fib4_entry, replace,
599cf8f9 2830 append);
b45f64d1 2831 if (err) {
9aecce1c
IS
2832 dev_warn(mlxsw_sp->bus_info->dev, "Failed to link FIB entry to node\n");
2833 goto err_fib4_node_entry_link;
b45f64d1 2834 }
9aecce1c 2835
4f1c7f1f 2836 mlxsw_sp_fib4_entry_replace(mlxsw_sp, fib4_entry, replace);
599cf8f9 2837
61c503f9
JP
2838 return 0;
2839
9aecce1c 2840err_fib4_node_entry_link:
4f1c7f1f 2841 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
9aecce1c 2842err_fib4_entry_create:
731ea1ca 2843 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
61c503f9
JP
2844 return err;
2845}
2846
37956d78
JP
2847static void mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp,
2848 struct fib_entry_notifier_info *fen_info)
61c503f9 2849{
4f1c7f1f 2850 struct mlxsw_sp_fib4_entry *fib4_entry;
9aecce1c 2851 struct mlxsw_sp_fib_node *fib_node;
61c503f9 2852
9011b677 2853 if (mlxsw_sp->router->aborted)
37956d78 2854 return;
b45f64d1 2855
4f1c7f1f
IS
2856 fib4_entry = mlxsw_sp_fib4_entry_lookup(mlxsw_sp, fen_info);
2857 if (WARN_ON(!fib4_entry))
37956d78 2858 return;
4f1c7f1f 2859 fib_node = fib4_entry->common.fib_node;
5b004412 2860
4f1c7f1f
IS
2861 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
2862 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
731ea1ca 2863 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
61c503f9 2864}
b45f64d1 2865
bc65a8a4
IS
2866static int __mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp,
2867 enum mlxsw_reg_ralxx_protocol proto,
2868 u8 tree_id)
b45f64d1
JP
2869{
2870 char ralta_pl[MLXSW_REG_RALTA_LEN];
2871 char ralst_pl[MLXSW_REG_RALST_LEN];
b5d90e6d 2872 int i, err;
b45f64d1 2873
bc65a8a4 2874 mlxsw_reg_ralta_pack(ralta_pl, true, proto, tree_id);
b45f64d1
JP
2875 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
2876 if (err)
2877 return err;
2878
bc65a8a4 2879 mlxsw_reg_ralst_pack(ralst_pl, 0xff, tree_id);
b45f64d1
JP
2880 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
2881 if (err)
2882 return err;
2883
b5d90e6d 2884 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
9011b677 2885 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
b5d90e6d
IS
2886 char raltb_pl[MLXSW_REG_RALTB_LEN];
2887 char ralue_pl[MLXSW_REG_RALUE_LEN];
b45f64d1 2888
b5d90e6d
IS
2889 if (!mlxsw_sp_vr_is_used(vr))
2890 continue;
2891
bc65a8a4 2892 mlxsw_reg_raltb_pack(raltb_pl, vr->id, proto, tree_id);
b5d90e6d
IS
2893 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb),
2894 raltb_pl);
2895 if (err)
2896 return err;
2897
bc65a8a4
IS
2898 mlxsw_reg_ralue_pack(ralue_pl, proto,
2899 MLXSW_REG_RALUE_OP_WRITE_WRITE, vr->id, 0);
b5d90e6d
IS
2900 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
2901 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue),
2902 ralue_pl);
2903 if (err)
2904 return err;
2905 }
2906
2907 return 0;
b45f64d1
JP
2908}
2909
bc65a8a4
IS
2910static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp)
2911{
2912 enum mlxsw_reg_ralxx_protocol proto = MLXSW_REG_RALXX_PROTOCOL_IPV4;
2913 int err;
2914
2915 err = __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
2916 MLXSW_SP_LPM_TREE_MIN);
2917 if (err)
2918 return err;
2919
2920 proto = MLXSW_REG_RALXX_PROTOCOL_IPV6;
2921 return __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
2922 MLXSW_SP_LPM_TREE_MIN + 1);
2923}
2924
9aecce1c
IS
2925static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp,
2926 struct mlxsw_sp_fib_node *fib_node)
2927{
4f1c7f1f 2928 struct mlxsw_sp_fib4_entry *fib4_entry, *tmp;
9aecce1c 2929
4f1c7f1f
IS
2930 list_for_each_entry_safe(fib4_entry, tmp, &fib_node->entry_list,
2931 common.list) {
2932 bool do_break = &tmp->common.list == &fib_node->entry_list;
9aecce1c 2933
4f1c7f1f
IS
2934 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
2935 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
731ea1ca 2936 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
9aecce1c
IS
2937 /* Break when entry list is empty and node was freed.
2938 * Otherwise, we'll access freed memory in the next
2939 * iteration.
2940 */
2941 if (do_break)
2942 break;
2943 }
2944}
2945
2946static void mlxsw_sp_fib_node_flush(struct mlxsw_sp *mlxsw_sp,
2947 struct mlxsw_sp_fib_node *fib_node)
2948{
76610ebb 2949 switch (fib_node->fib->proto) {
9aecce1c
IS
2950 case MLXSW_SP_L3_PROTO_IPV4:
2951 mlxsw_sp_fib4_node_flush(mlxsw_sp, fib_node);
2952 break;
2953 case MLXSW_SP_L3_PROTO_IPV6:
2954 WARN_ON_ONCE(1);
2955 break;
2956 }
2957}
2958
76610ebb
IS
2959static void mlxsw_sp_vr_fib_flush(struct mlxsw_sp *mlxsw_sp,
2960 struct mlxsw_sp_vr *vr,
2961 enum mlxsw_sp_l3proto proto)
b45f64d1 2962{
76610ebb 2963 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
9aecce1c 2964 struct mlxsw_sp_fib_node *fib_node, *tmp;
76610ebb
IS
2965
2966 list_for_each_entry_safe(fib_node, tmp, &fib->node_list, list) {
2967 bool do_break = &tmp->list == &fib->node_list;
2968
2969 mlxsw_sp_fib_node_flush(mlxsw_sp, fib_node);
2970 if (do_break)
2971 break;
2972 }
2973}
2974
2975static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp)
2976{
b45f64d1 2977 int i;
b45f64d1 2978
c1a38311 2979 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
9011b677 2980 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
ac571de9 2981
76610ebb 2982 if (!mlxsw_sp_vr_is_used(vr))
b45f64d1 2983 continue;
76610ebb 2984 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV4);
a3d9bc50
IS
2985
2986 /* If virtual router was only used for IPv4, then it's no
2987 * longer used.
2988 */
2989 if (!mlxsw_sp_vr_is_used(vr))
2990 continue;
2991 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV6);
b45f64d1 2992 }
ac571de9
IS
2993}
2994
bc65a8a4 2995static void mlxsw_sp_router_fib_abort(struct mlxsw_sp *mlxsw_sp)
ac571de9
IS
2996{
2997 int err;
2998
9011b677 2999 if (mlxsw_sp->router->aborted)
d331d303
IS
3000 return;
3001 dev_warn(mlxsw_sp->bus_info->dev, "FIB abort triggered. Note that FIB entries are no longer being offloaded to this device.\n");
ac571de9 3002 mlxsw_sp_router_fib_flush(mlxsw_sp);
9011b677 3003 mlxsw_sp->router->aborted = true;
b45f64d1
JP
3004 err = mlxsw_sp_router_set_abort_trap(mlxsw_sp);
3005 if (err)
3006 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set abort trap.\n");
3007}
3008
3057224e 3009struct mlxsw_sp_fib_event_work {
a0e4761d 3010 struct work_struct work;
ad178c8e
IS
3011 union {
3012 struct fib_entry_notifier_info fen_info;
5d7bfd14 3013 struct fib_rule_notifier_info fr_info;
ad178c8e
IS
3014 struct fib_nh_notifier_info fnh_info;
3015 };
3057224e
IS
3016 struct mlxsw_sp *mlxsw_sp;
3017 unsigned long event;
3018};
3019
3020static void mlxsw_sp_router_fib_event_work(struct work_struct *work)
b45f64d1 3021{
3057224e 3022 struct mlxsw_sp_fib_event_work *fib_work =
a0e4761d 3023 container_of(work, struct mlxsw_sp_fib_event_work, work);
3057224e 3024 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
5d7bfd14 3025 struct fib_rule *rule;
599cf8f9 3026 bool replace, append;
b45f64d1
JP
3027 int err;
3028
3057224e
IS
3029 /* Protect internal structures from changes */
3030 rtnl_lock();
3031 switch (fib_work->event) {
599cf8f9 3032 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
4283bce5 3033 case FIB_EVENT_ENTRY_APPEND: /* fall through */
b45f64d1 3034 case FIB_EVENT_ENTRY_ADD:
599cf8f9 3035 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
4283bce5
IS
3036 append = fib_work->event == FIB_EVENT_ENTRY_APPEND;
3037 err = mlxsw_sp_router_fib4_add(mlxsw_sp, &fib_work->fen_info,
599cf8f9 3038 replace, append);
b45f64d1 3039 if (err)
bc65a8a4 3040 mlxsw_sp_router_fib_abort(mlxsw_sp);
3057224e 3041 fib_info_put(fib_work->fen_info.fi);
b45f64d1
JP
3042 break;
3043 case FIB_EVENT_ENTRY_DEL:
3057224e
IS
3044 mlxsw_sp_router_fib4_del(mlxsw_sp, &fib_work->fen_info);
3045 fib_info_put(fib_work->fen_info.fi);
b45f64d1
JP
3046 break;
3047 case FIB_EVENT_RULE_ADD: /* fall through */
3048 case FIB_EVENT_RULE_DEL:
5d7bfd14 3049 rule = fib_work->fr_info.rule;
c7f6e665 3050 if (!fib4_rule_default(rule) && !rule->l3mdev)
bc65a8a4 3051 mlxsw_sp_router_fib_abort(mlxsw_sp);
5d7bfd14 3052 fib_rule_put(rule);
b45f64d1 3053 break;
ad178c8e
IS
3054 case FIB_EVENT_NH_ADD: /* fall through */
3055 case FIB_EVENT_NH_DEL:
0e6ea2a4
IS
3056 mlxsw_sp_nexthop4_event(mlxsw_sp, fib_work->event,
3057 fib_work->fnh_info.fib_nh);
ad178c8e
IS
3058 fib_info_put(fib_work->fnh_info.fib_nh->nh_parent);
3059 break;
b45f64d1 3060 }
3057224e
IS
3061 rtnl_unlock();
3062 kfree(fib_work);
3063}
3064
3065/* Called with rcu_read_lock() */
3066static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
3067 unsigned long event, void *ptr)
3068{
3057224e
IS
3069 struct mlxsw_sp_fib_event_work *fib_work;
3070 struct fib_notifier_info *info = ptr;
7e39d115 3071 struct mlxsw_sp_router *router;
3057224e
IS
3072
3073 if (!net_eq(info->net, &init_net))
3074 return NOTIFY_DONE;
3075
3076 fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC);
3077 if (WARN_ON(!fib_work))
3078 return NOTIFY_BAD;
3079
a0e4761d 3080 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib_event_work);
7e39d115
IS
3081 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
3082 fib_work->mlxsw_sp = router->mlxsw_sp;
3057224e
IS
3083 fib_work->event = event;
3084
3085 switch (event) {
599cf8f9 3086 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
4283bce5 3087 case FIB_EVENT_ENTRY_APPEND: /* fall through */
3057224e
IS
3088 case FIB_EVENT_ENTRY_ADD: /* fall through */
3089 case FIB_EVENT_ENTRY_DEL:
3090 memcpy(&fib_work->fen_info, ptr, sizeof(fib_work->fen_info));
3091 /* Take referece on fib_info to prevent it from being
3092 * freed while work is queued. Release it afterwards.
3093 */
3094 fib_info_hold(fib_work->fen_info.fi);
3095 break;
5d7bfd14
IS
3096 case FIB_EVENT_RULE_ADD: /* fall through */
3097 case FIB_EVENT_RULE_DEL:
3098 memcpy(&fib_work->fr_info, ptr, sizeof(fib_work->fr_info));
3099 fib_rule_get(fib_work->fr_info.rule);
3100 break;
ad178c8e
IS
3101 case FIB_EVENT_NH_ADD: /* fall through */
3102 case FIB_EVENT_NH_DEL:
3103 memcpy(&fib_work->fnh_info, ptr, sizeof(fib_work->fnh_info));
3104 fib_info_hold(fib_work->fnh_info.fib_nh->nh_parent);
3105 break;
3057224e
IS
3106 }
3107
a0e4761d 3108 mlxsw_core_schedule_work(&fib_work->work);
3057224e 3109
b45f64d1
JP
3110 return NOTIFY_DONE;
3111}
3112
4724ba56
IS
3113static struct mlxsw_sp_rif *
3114mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
3115 const struct net_device *dev)
3116{
3117 int i;
3118
3119 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
5f9efffb
IS
3120 if (mlxsw_sp->router->rifs[i] &&
3121 mlxsw_sp->router->rifs[i]->dev == dev)
3122 return mlxsw_sp->router->rifs[i];
4724ba56
IS
3123
3124 return NULL;
3125}
3126
3127static int mlxsw_sp_router_rif_disable(struct mlxsw_sp *mlxsw_sp, u16 rif)
3128{
3129 char ritr_pl[MLXSW_REG_RITR_LEN];
3130 int err;
3131
3132 mlxsw_reg_ritr_rif_pack(ritr_pl, rif);
3133 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
3134 if (WARN_ON_ONCE(err))
3135 return err;
3136
3137 mlxsw_reg_ritr_enable_set(ritr_pl, false);
3138 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
3139}
3140
3141static void mlxsw_sp_router_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
bf95233e 3142 struct mlxsw_sp_rif *rif)
4724ba56 3143{
bf95233e
AS
3144 mlxsw_sp_router_rif_disable(mlxsw_sp, rif->rif_index);
3145 mlxsw_sp_nexthop_rif_gone_sync(mlxsw_sp, rif);
3146 mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, rif);
4724ba56
IS
3147}
3148
5ea1237f
AS
3149static bool
3150mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *rif, struct net_device *dev,
3151 unsigned long event)
4724ba56 3152{
5ea1237f
AS
3153 struct inet6_dev *inet6_dev;
3154 bool addr_list_empty = true;
3155 struct in_device *idev;
3156
4724ba56
IS
3157 switch (event) {
3158 case NETDEV_UP:
f1b1f273 3159 return rif == NULL;
4724ba56 3160 case NETDEV_DOWN:
5ea1237f
AS
3161 idev = __in_dev_get_rtnl(dev);
3162 if (idev && idev->ifa_list)
3163 addr_list_empty = false;
3164
3165 inet6_dev = __in6_dev_get(dev);
3166 if (addr_list_empty && inet6_dev &&
3167 !list_empty(&inet6_dev->addr_list))
3168 addr_list_empty = false;
3169
3170 if (rif && addr_list_empty &&
bf95233e 3171 !netif_is_l3_slave(rif->dev))
4724ba56
IS
3172 return true;
3173 /* It is possible we already removed the RIF ourselves
3174 * if it was assigned to a netdev that is now a bridge
3175 * or LAG slave.
3176 */
3177 return false;
3178 }
3179
3180 return false;
3181}
3182
e4f3c1c1
IS
3183static enum mlxsw_sp_rif_type
3184mlxsw_sp_dev_rif_type(const struct mlxsw_sp *mlxsw_sp,
3185 const struct net_device *dev)
3186{
3187 enum mlxsw_sp_fid_type type;
3188
3189 /* RIF type is derived from the type of the underlying FID */
3190 if (is_vlan_dev(dev) && netif_is_bridge_master(vlan_dev_real_dev(dev)))
3191 type = MLXSW_SP_FID_TYPE_8021Q;
3192 else if (netif_is_bridge_master(dev) && br_vlan_enabled(dev))
3193 type = MLXSW_SP_FID_TYPE_8021Q;
3194 else if (netif_is_bridge_master(dev))
3195 type = MLXSW_SP_FID_TYPE_8021D;
3196 else
3197 type = MLXSW_SP_FID_TYPE_RFID;
3198
3199 return mlxsw_sp_fid_type_rif_type(mlxsw_sp, type);
3200}
3201
de5ed99e 3202static int mlxsw_sp_rif_index_alloc(struct mlxsw_sp *mlxsw_sp, u16 *p_rif_index)
4724ba56
IS
3203{
3204 int i;
3205
de5ed99e
IS
3206 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) {
3207 if (!mlxsw_sp->router->rifs[i]) {
3208 *p_rif_index = i;
3209 return 0;
3210 }
3211 }
4724ba56 3212
de5ed99e 3213 return -ENOBUFS;
4724ba56
IS
3214}
3215
e4f3c1c1
IS
3216static struct mlxsw_sp_rif *mlxsw_sp_rif_alloc(size_t rif_size, u16 rif_index,
3217 u16 vr_id,
3218 struct net_device *l3_dev)
4724ba56 3219{
bf95233e 3220 struct mlxsw_sp_rif *rif;
4724ba56 3221
e4f3c1c1 3222 rif = kzalloc(rif_size, GFP_KERNEL);
bf95233e 3223 if (!rif)
4724ba56
IS
3224 return NULL;
3225
bf95233e
AS
3226 INIT_LIST_HEAD(&rif->nexthop_list);
3227 INIT_LIST_HEAD(&rif->neigh_list);
3228 ether_addr_copy(rif->addr, l3_dev->dev_addr);
3229 rif->mtu = l3_dev->mtu;
3230 rif->vr_id = vr_id;
3231 rif->dev = l3_dev;
3232 rif->rif_index = rif_index;
4724ba56 3233
bf95233e 3234 return rif;
4724ba56
IS
3235}
3236
5f9efffb
IS
3237struct mlxsw_sp_rif *mlxsw_sp_rif_by_index(const struct mlxsw_sp *mlxsw_sp,
3238 u16 rif_index)
3239{
3240 return mlxsw_sp->router->rifs[rif_index];
3241}
3242
fd1b9d41
AS
3243u16 mlxsw_sp_rif_index(const struct mlxsw_sp_rif *rif)
3244{
3245 return rif->rif_index;
3246}
3247
3248int mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif)
3249{
3250 return rif->dev->ifindex;
3251}
3252
4724ba56 3253static struct mlxsw_sp_rif *
e4f3c1c1
IS
3254mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
3255 const struct mlxsw_sp_rif_params *params)
4724ba56 3256{
e4f3c1c1
IS
3257 u32 tb_id = l3mdev_fib_table(params->dev);
3258 const struct mlxsw_sp_rif_ops *ops;
3259 enum mlxsw_sp_rif_type type;
bf95233e 3260 struct mlxsw_sp_rif *rif;
a1107487
IS
3261 struct mlxsw_sp_fid *fid;
3262 struct mlxsw_sp_vr *vr;
3263 u16 rif_index;
4724ba56
IS
3264 int err;
3265
e4f3c1c1
IS
3266 type = mlxsw_sp_dev_rif_type(mlxsw_sp, params->dev);
3267 ops = mlxsw_sp->router->rif_ops_arr[type];
3268
c9ec53f0
IS
3269 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id ? : RT_TABLE_MAIN);
3270 if (IS_ERR(vr))
3271 return ERR_CAST(vr);
3272
de5ed99e
IS
3273 err = mlxsw_sp_rif_index_alloc(mlxsw_sp, &rif_index);
3274 if (err)
3275 goto err_rif_index_alloc;
4724ba56 3276
e4f3c1c1 3277 rif = mlxsw_sp_rif_alloc(ops->rif_size, rif_index, vr->id, params->dev);
a13a594d
IS
3278 if (!rif) {
3279 err = -ENOMEM;
3280 goto err_rif_alloc;
3281 }
e4f3c1c1
IS
3282 rif->mlxsw_sp = mlxsw_sp;
3283 rif->ops = ops;
a13a594d 3284
e4f3c1c1
IS
3285 fid = ops->fid_get(rif);
3286 if (IS_ERR(fid)) {
3287 err = PTR_ERR(fid);
3288 goto err_fid_get;
4d93ceeb 3289 }
e4f3c1c1 3290 rif->fid = fid;
4d93ceeb 3291
e4f3c1c1
IS
3292 if (ops->setup)
3293 ops->setup(rif, params);
3294
3295 err = ops->configure(rif);
4724ba56 3296 if (err)
e4f3c1c1 3297 goto err_configure;
4724ba56 3298
e4f3c1c1 3299 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, params->dev->dev_addr,
a1107487 3300 mlxsw_sp_fid_index(fid), true);
4724ba56
IS
3301 if (err)
3302 goto err_rif_fdb_op;
3303
e4f3c1c1 3304 mlxsw_sp_rif_counters_alloc(rif);
a1107487 3305 mlxsw_sp_fid_rif_set(fid, rif);
5f9efffb 3306 mlxsw_sp->router->rifs[rif_index] = rif;
6913229e 3307 vr->rif_count++;
4724ba56 3308
bf95233e 3309 return rif;
4724ba56 3310
4724ba56 3311err_rif_fdb_op:
e4f3c1c1
IS
3312 ops->deconfigure(rif);
3313err_configure:
a1107487
IS
3314 mlxsw_sp_fid_put(fid);
3315err_fid_get:
e4f3c1c1
IS
3316 kfree(rif);
3317err_rif_alloc:
de5ed99e 3318err_rif_index_alloc:
c9ec53f0 3319 mlxsw_sp_vr_put(vr);
4724ba56
IS
3320 return ERR_PTR(err);
3321}
3322
e4f3c1c1 3323void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif)
4724ba56 3324{
e4f3c1c1
IS
3325 const struct mlxsw_sp_rif_ops *ops = rif->ops;
3326 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
a1107487 3327 struct mlxsw_sp_fid *fid = rif->fid;
e4f3c1c1 3328 struct mlxsw_sp_vr *vr;
4724ba56 3329
bf95233e 3330 mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif);
e4f3c1c1 3331 vr = &mlxsw_sp->router->vrs[rif->vr_id];
e0c0afd8 3332
6913229e 3333 vr->rif_count--;
e4f3c1c1 3334 mlxsw_sp->router->rifs[rif->rif_index] = NULL;
a1107487 3335 mlxsw_sp_fid_rif_set(fid, NULL);
e4f3c1c1
IS
3336 mlxsw_sp_rif_counters_free(rif);
3337 mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->dev->dev_addr,
3338 mlxsw_sp_fid_index(fid), false);
3339 ops->deconfigure(rif);
a1107487 3340 mlxsw_sp_fid_put(fid);
e4f3c1c1 3341 kfree(rif);
c9ec53f0 3342 mlxsw_sp_vr_put(vr);
4724ba56
IS
3343}
3344
e4f3c1c1
IS
3345static void
3346mlxsw_sp_rif_subport_params_init(struct mlxsw_sp_rif_params *params,
3347 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
3348{
3349 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
3350
3351 params->vid = mlxsw_sp_port_vlan->vid;
3352 params->lag = mlxsw_sp_port->lagged;
3353 if (params->lag)
3354 params->lag_id = mlxsw_sp_port->lag_id;
3355 else
3356 params->system_port = mlxsw_sp_port->local_port;
3357}
3358
7cbecf24 3359static int
a1107487 3360mlxsw_sp_port_vlan_router_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
7cbecf24 3361 struct net_device *l3_dev)
4724ba56 3362{
7cbecf24 3363 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
1b8f09a0 3364 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
7cbecf24 3365 u16 vid = mlxsw_sp_port_vlan->vid;
bf95233e 3366 struct mlxsw_sp_rif *rif;
a1107487 3367 struct mlxsw_sp_fid *fid;
03ea01e9 3368 int err;
4724ba56 3369
1b8f09a0 3370 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
bf95233e 3371 if (!rif) {
e4f3c1c1
IS
3372 struct mlxsw_sp_rif_params params = {
3373 .dev = l3_dev,
3374 };
3375
3376 mlxsw_sp_rif_subport_params_init(&params, mlxsw_sp_port_vlan);
3377 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
bf95233e
AS
3378 if (IS_ERR(rif))
3379 return PTR_ERR(rif);
4724ba56
IS
3380 }
3381
a1107487 3382 /* FID was already created, just take a reference */
e4f3c1c1 3383 fid = rif->ops->fid_get(rif);
a1107487
IS
3384 err = mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port, vid);
3385 if (err)
3386 goto err_fid_port_vid_map;
3387
7cbecf24 3388 err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false);
03ea01e9
IS
3389 if (err)
3390 goto err_port_vid_learning_set;
3391
7cbecf24 3392 err = mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid,
03ea01e9
IS
3393 BR_STATE_FORWARDING);
3394 if (err)
3395 goto err_port_vid_stp_set;
3396
a1107487 3397 mlxsw_sp_port_vlan->fid = fid;
4724ba56 3398
4724ba56 3399 return 0;
03ea01e9
IS
3400
3401err_port_vid_stp_set:
7cbecf24 3402 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
03ea01e9 3403err_port_vid_learning_set:
a1107487
IS
3404 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
3405err_fid_port_vid_map:
3406 mlxsw_sp_fid_put(fid);
03ea01e9 3407 return err;
4724ba56
IS
3408}
3409
a1107487
IS
3410void
3411mlxsw_sp_port_vlan_router_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
4724ba56 3412{
ce95e154 3413 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
7cbecf24 3414 struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid;
ce95e154 3415 u16 vid = mlxsw_sp_port_vlan->vid;
ce95e154 3416
a1107487
IS
3417 if (WARN_ON(mlxsw_sp_fid_type(fid) != MLXSW_SP_FID_TYPE_RFID))
3418 return;
4aafc368 3419
a1107487 3420 mlxsw_sp_port_vlan->fid = NULL;
7cbecf24
IS
3421 mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, BR_STATE_BLOCKING);
3422 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
a1107487
IS
3423 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
3424 /* If router port holds the last reference on the rFID, then the
3425 * associated Sub-port RIF will be destroyed.
3426 */
3427 mlxsw_sp_fid_put(fid);
4724ba56
IS
3428}
3429
7cbecf24
IS
3430static int mlxsw_sp_inetaddr_port_vlan_event(struct net_device *l3_dev,
3431 struct net_device *port_dev,
3432 unsigned long event, u16 vid)
4724ba56
IS
3433{
3434 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(port_dev);
ce95e154 3435 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
4724ba56 3436
ce95e154 3437 mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
7cbecf24
IS
3438 if (WARN_ON(!mlxsw_sp_port_vlan))
3439 return -EINVAL;
4724ba56
IS
3440
3441 switch (event) {
3442 case NETDEV_UP:
a1107487 3443 return mlxsw_sp_port_vlan_router_join(mlxsw_sp_port_vlan,
7cbecf24 3444 l3_dev);
4724ba56 3445 case NETDEV_DOWN:
a1107487 3446 mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);
4724ba56
IS
3447 break;
3448 }
3449
3450 return 0;
3451}
3452
3453static int mlxsw_sp_inetaddr_port_event(struct net_device *port_dev,
3454 unsigned long event)
3455{
2b94e58d
JP
3456 if (netif_is_bridge_port(port_dev) ||
3457 netif_is_lag_port(port_dev) ||
3458 netif_is_ovs_port(port_dev))
4724ba56
IS
3459 return 0;
3460
7cbecf24 3461 return mlxsw_sp_inetaddr_port_vlan_event(port_dev, port_dev, event, 1);
4724ba56
IS
3462}
3463
3464static int __mlxsw_sp_inetaddr_lag_event(struct net_device *l3_dev,
3465 struct net_device *lag_dev,
3466 unsigned long event, u16 vid)
3467{
3468 struct net_device *port_dev;
3469 struct list_head *iter;
3470 int err;
3471
3472 netdev_for_each_lower_dev(lag_dev, port_dev, iter) {
3473 if (mlxsw_sp_port_dev_check(port_dev)) {
7cbecf24
IS
3474 err = mlxsw_sp_inetaddr_port_vlan_event(l3_dev,
3475 port_dev,
3476 event, vid);
4724ba56
IS
3477 if (err)
3478 return err;
3479 }
3480 }
3481
3482 return 0;
3483}
3484
3485static int mlxsw_sp_inetaddr_lag_event(struct net_device *lag_dev,
3486 unsigned long event)
3487{
3488 if (netif_is_bridge_port(lag_dev))
3489 return 0;
3490
3491 return __mlxsw_sp_inetaddr_lag_event(lag_dev, lag_dev, event, 1);
3492}
3493
4724ba56 3494static int mlxsw_sp_inetaddr_bridge_event(struct net_device *l3_dev,
4724ba56
IS
3495 unsigned long event)
3496{
3497 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
e4f3c1c1
IS
3498 struct mlxsw_sp_rif_params params = {
3499 .dev = l3_dev,
3500 };
a1107487 3501 struct mlxsw_sp_rif *rif;
4724ba56
IS
3502
3503 switch (event) {
3504 case NETDEV_UP:
e4f3c1c1
IS
3505 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
3506 if (IS_ERR(rif))
3507 return PTR_ERR(rif);
3508 break;
4724ba56 3509 case NETDEV_DOWN:
a1107487 3510 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
e4f3c1c1 3511 mlxsw_sp_rif_destroy(rif);
4724ba56
IS
3512 break;
3513 }
3514
3515 return 0;
3516}
3517
3518static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev,
3519 unsigned long event)
3520{
3521 struct net_device *real_dev = vlan_dev_real_dev(vlan_dev);
4724ba56
IS
3522 u16 vid = vlan_dev_vlan_id(vlan_dev);
3523
6b27c8ad
IS
3524 if (netif_is_bridge_port(vlan_dev))
3525 return 0;
3526
4724ba56 3527 if (mlxsw_sp_port_dev_check(real_dev))
7cbecf24
IS
3528 return mlxsw_sp_inetaddr_port_vlan_event(vlan_dev, real_dev,
3529 event, vid);
4724ba56
IS
3530 else if (netif_is_lag_master(real_dev))
3531 return __mlxsw_sp_inetaddr_lag_event(vlan_dev, real_dev, event,
3532 vid);
c57529e1 3533 else if (netif_is_bridge_master(real_dev) && br_vlan_enabled(real_dev))
a1107487 3534 return mlxsw_sp_inetaddr_bridge_event(vlan_dev, event);
4724ba56
IS
3535
3536 return 0;
3537}
3538
b1e45526
IS
3539static int __mlxsw_sp_inetaddr_event(struct net_device *dev,
3540 unsigned long event)
3541{
3542 if (mlxsw_sp_port_dev_check(dev))
3543 return mlxsw_sp_inetaddr_port_event(dev, event);
3544 else if (netif_is_lag_master(dev))
3545 return mlxsw_sp_inetaddr_lag_event(dev, event);
3546 else if (netif_is_bridge_master(dev))
a1107487 3547 return mlxsw_sp_inetaddr_bridge_event(dev, event);
b1e45526
IS
3548 else if (is_vlan_dev(dev))
3549 return mlxsw_sp_inetaddr_vlan_event(dev, event);
3550 else
3551 return 0;
3552}
3553
4724ba56
IS
3554int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
3555 unsigned long event, void *ptr)
3556{
3557 struct in_ifaddr *ifa = (struct in_ifaddr *) ptr;
3558 struct net_device *dev = ifa->ifa_dev->dev;
3559 struct mlxsw_sp *mlxsw_sp;
bf95233e 3560 struct mlxsw_sp_rif *rif;
4724ba56
IS
3561 int err = 0;
3562
3563 mlxsw_sp = mlxsw_sp_lower_get(dev);
3564 if (!mlxsw_sp)
3565 goto out;
3566
bf95233e 3567 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
5ea1237f 3568 if (!mlxsw_sp_rif_should_config(rif, dev, event))
4724ba56
IS
3569 goto out;
3570
b1e45526 3571 err = __mlxsw_sp_inetaddr_event(dev, event);
4724ba56
IS
3572out:
3573 return notifier_from_errno(err);
3574}
3575
5ea1237f
AS
3576struct mlxsw_sp_inet6addr_event_work {
3577 struct work_struct work;
3578 struct net_device *dev;
3579 unsigned long event;
3580};
3581
3582static void mlxsw_sp_inet6addr_event_work(struct work_struct *work)
3583{
3584 struct mlxsw_sp_inet6addr_event_work *inet6addr_work =
3585 container_of(work, struct mlxsw_sp_inet6addr_event_work, work);
3586 struct net_device *dev = inet6addr_work->dev;
3587 unsigned long event = inet6addr_work->event;
3588 struct mlxsw_sp *mlxsw_sp;
3589 struct mlxsw_sp_rif *rif;
3590
3591 rtnl_lock();
3592 mlxsw_sp = mlxsw_sp_lower_get(dev);
3593 if (!mlxsw_sp)
3594 goto out;
3595
3596 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
3597 if (!mlxsw_sp_rif_should_config(rif, dev, event))
3598 goto out;
3599
3600 __mlxsw_sp_inetaddr_event(dev, event);
3601out:
3602 rtnl_unlock();
3603 dev_put(dev);
3604 kfree(inet6addr_work);
3605}
3606
3607/* Called with rcu_read_lock() */
3608int mlxsw_sp_inet6addr_event(struct notifier_block *unused,
3609 unsigned long event, void *ptr)
3610{
3611 struct inet6_ifaddr *if6 = (struct inet6_ifaddr *) ptr;
3612 struct mlxsw_sp_inet6addr_event_work *inet6addr_work;
3613 struct net_device *dev = if6->idev->dev;
3614
3615 if (!mlxsw_sp_port_dev_lower_find_rcu(dev))
3616 return NOTIFY_DONE;
3617
3618 inet6addr_work = kzalloc(sizeof(*inet6addr_work), GFP_ATOMIC);
3619 if (!inet6addr_work)
3620 return NOTIFY_BAD;
3621
3622 INIT_WORK(&inet6addr_work->work, mlxsw_sp_inet6addr_event_work);
3623 inet6addr_work->dev = dev;
3624 inet6addr_work->event = event;
3625 dev_hold(dev);
3626 mlxsw_core_schedule_work(&inet6addr_work->work);
3627
3628 return NOTIFY_DONE;
3629}
3630
bf95233e 3631static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
4724ba56
IS
3632 const char *mac, int mtu)
3633{
3634 char ritr_pl[MLXSW_REG_RITR_LEN];
3635 int err;
3636
bf95233e 3637 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
4724ba56
IS
3638 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
3639 if (err)
3640 return err;
3641
3642 mlxsw_reg_ritr_mtu_set(ritr_pl, mtu);
3643 mlxsw_reg_ritr_if_mac_memcpy_to(ritr_pl, mac);
3644 mlxsw_reg_ritr_op_set(ritr_pl, MLXSW_REG_RITR_RIF_CREATE);
3645 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
3646}
3647
3648int mlxsw_sp_netdevice_router_port_event(struct net_device *dev)
3649{
3650 struct mlxsw_sp *mlxsw_sp;
bf95233e 3651 struct mlxsw_sp_rif *rif;
a1107487 3652 u16 fid_index;
4724ba56
IS
3653 int err;
3654
3655 mlxsw_sp = mlxsw_sp_lower_get(dev);
3656 if (!mlxsw_sp)
3657 return 0;
3658
bf95233e
AS
3659 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
3660 if (!rif)
4724ba56 3661 return 0;
a1107487 3662 fid_index = mlxsw_sp_fid_index(rif->fid);
4724ba56 3663
a1107487 3664 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, false);
4724ba56
IS
3665 if (err)
3666 return err;
3667
bf95233e
AS
3668 err = mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, dev->dev_addr,
3669 dev->mtu);
4724ba56
IS
3670 if (err)
3671 goto err_rif_edit;
3672
a1107487 3673 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, dev->dev_addr, fid_index, true);
4724ba56
IS
3674 if (err)
3675 goto err_rif_fdb_op;
3676
bf95233e
AS
3677 ether_addr_copy(rif->addr, dev->dev_addr);
3678 rif->mtu = dev->mtu;
4724ba56 3679
bf95233e 3680 netdev_dbg(dev, "Updated RIF=%d\n", rif->rif_index);
4724ba56
IS
3681
3682 return 0;
3683
3684err_rif_fdb_op:
bf95233e 3685 mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, rif->addr, rif->mtu);
4724ba56 3686err_rif_edit:
a1107487 3687 mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, true);
4724ba56
IS
3688 return err;
3689}
3690
b1e45526
IS
3691static int mlxsw_sp_port_vrf_join(struct mlxsw_sp *mlxsw_sp,
3692 struct net_device *l3_dev)
7179eb5a 3693{
b1e45526 3694 struct mlxsw_sp_rif *rif;
7179eb5a 3695
b1e45526
IS
3696 /* If netdev is already associated with a RIF, then we need to
3697 * destroy it and create a new one with the new virtual router ID.
7179eb5a 3698 */
b1e45526
IS
3699 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
3700 if (rif)
3701 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
7179eb5a 3702
b1e45526 3703 return __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_UP);
7179eb5a
IS
3704}
3705
b1e45526
IS
3706static void mlxsw_sp_port_vrf_leave(struct mlxsw_sp *mlxsw_sp,
3707 struct net_device *l3_dev)
7179eb5a 3708{
b1e45526 3709 struct mlxsw_sp_rif *rif;
7179eb5a 3710
b1e45526
IS
3711 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
3712 if (!rif)
7179eb5a 3713 return;
b1e45526 3714 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
7179eb5a
IS
3715}
3716
b1e45526
IS
3717int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
3718 struct netdev_notifier_changeupper_info *info)
3d70e458 3719{
b1e45526
IS
3720 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
3721 int err = 0;
3d70e458 3722
b1e45526
IS
3723 if (!mlxsw_sp)
3724 return 0;
3d70e458 3725
b1e45526
IS
3726 switch (event) {
3727 case NETDEV_PRECHANGEUPPER:
3728 return 0;
3729 case NETDEV_CHANGEUPPER:
3730 if (info->linking)
3731 err = mlxsw_sp_port_vrf_join(mlxsw_sp, l3_dev);
3732 else
3733 mlxsw_sp_port_vrf_leave(mlxsw_sp, l3_dev);
3734 break;
3735 }
3d70e458 3736
b1e45526 3737 return err;
3d70e458
IS
3738}
3739
e4f3c1c1
IS
3740static struct mlxsw_sp_rif_subport *
3741mlxsw_sp_rif_subport_rif(const struct mlxsw_sp_rif *rif)
a1107487 3742{
e4f3c1c1
IS
3743 return container_of(rif, struct mlxsw_sp_rif_subport, common);
3744}
3745
3746static void mlxsw_sp_rif_subport_setup(struct mlxsw_sp_rif *rif,
3747 const struct mlxsw_sp_rif_params *params)
3748{
3749 struct mlxsw_sp_rif_subport *rif_subport;
3750
3751 rif_subport = mlxsw_sp_rif_subport_rif(rif);
3752 rif_subport->vid = params->vid;
3753 rif_subport->lag = params->lag;
3754 if (params->lag)
3755 rif_subport->lag_id = params->lag_id;
a1107487 3756 else
e4f3c1c1
IS
3757 rif_subport->system_port = params->system_port;
3758}
3759
3760static int mlxsw_sp_rif_subport_op(struct mlxsw_sp_rif *rif, bool enable)
3761{
3762 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
3763 struct mlxsw_sp_rif_subport *rif_subport;
3764 char ritr_pl[MLXSW_REG_RITR_LEN];
3765
3766 rif_subport = mlxsw_sp_rif_subport_rif(rif);
3767 mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_SP_IF,
3768 rif->rif_index, rif->vr_id, rif->dev->mtu,
3769 rif->dev->dev_addr);
3770 mlxsw_reg_ritr_sp_if_pack(ritr_pl, rif_subport->lag,
3771 rif_subport->lag ? rif_subport->lag_id :
3772 rif_subport->system_port,
3773 rif_subport->vid);
3774
3775 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
3776}
3777
3778static int mlxsw_sp_rif_subport_configure(struct mlxsw_sp_rif *rif)
3779{
3780 return mlxsw_sp_rif_subport_op(rif, true);
a1107487
IS
3781}
3782
e4f3c1c1
IS
3783static void mlxsw_sp_rif_subport_deconfigure(struct mlxsw_sp_rif *rif)
3784{
3785 mlxsw_sp_rif_subport_op(rif, false);
3786}
3787
3788static struct mlxsw_sp_fid *
3789mlxsw_sp_rif_subport_fid_get(struct mlxsw_sp_rif *rif)
3790{
3791 return mlxsw_sp_fid_rfid_get(rif->mlxsw_sp, rif->rif_index);
3792}
3793
3794static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_subport_ops = {
3795 .type = MLXSW_SP_RIF_TYPE_SUBPORT,
3796 .rif_size = sizeof(struct mlxsw_sp_rif_subport),
3797 .setup = mlxsw_sp_rif_subport_setup,
3798 .configure = mlxsw_sp_rif_subport_configure,
3799 .deconfigure = mlxsw_sp_rif_subport_deconfigure,
3800 .fid_get = mlxsw_sp_rif_subport_fid_get,
3801};
3802
3803static int mlxsw_sp_rif_vlan_fid_op(struct mlxsw_sp_rif *rif,
3804 enum mlxsw_reg_ritr_if_type type,
3805 u16 vid_fid, bool enable)
3806{
3807 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
3808 char ritr_pl[MLXSW_REG_RITR_LEN];
3809
3810 mlxsw_reg_ritr_pack(ritr_pl, enable, type, rif->rif_index, rif->vr_id,
3811 rif->dev->mtu, rif->dev->dev_addr);
3812 mlxsw_reg_ritr_fid_set(ritr_pl, type, vid_fid);
3813
3814 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
3815}
3816
3817static u8 mlxsw_sp_router_port(const struct mlxsw_sp *mlxsw_sp)
3818{
3819 return mlxsw_core_max_ports(mlxsw_sp->core) + 1;
3820}
3821
3822static int mlxsw_sp_rif_vlan_configure(struct mlxsw_sp_rif *rif)
3823{
3824 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
3825 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
3826 int err;
3827
3828 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, true);
3829 if (err)
3830 return err;
3831
0d284818
IS
3832 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
3833 mlxsw_sp_router_port(mlxsw_sp), true);
3834 if (err)
3835 goto err_fid_mc_flood_set;
3836
e4f3c1c1
IS
3837 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
3838 mlxsw_sp_router_port(mlxsw_sp), true);
3839 if (err)
3840 goto err_fid_bc_flood_set;
3841
3842 return 0;
3843
3844err_fid_bc_flood_set:
0d284818
IS
3845 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
3846 mlxsw_sp_router_port(mlxsw_sp), false);
3847err_fid_mc_flood_set:
e4f3c1c1
IS
3848 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
3849 return err;
3850}
3851
3852static void mlxsw_sp_rif_vlan_deconfigure(struct mlxsw_sp_rif *rif)
3853{
3854 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
3855 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
3856
3857 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
3858 mlxsw_sp_router_port(mlxsw_sp), false);
0d284818
IS
3859 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
3860 mlxsw_sp_router_port(mlxsw_sp), false);
e4f3c1c1
IS
3861 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
3862}
3863
3864static struct mlxsw_sp_fid *
3865mlxsw_sp_rif_vlan_fid_get(struct mlxsw_sp_rif *rif)
3866{
3867 u16 vid = is_vlan_dev(rif->dev) ? vlan_dev_vlan_id(rif->dev) : 1;
3868
3869 return mlxsw_sp_fid_8021q_get(rif->mlxsw_sp, vid);
3870}
3871
3872static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_vlan_ops = {
3873 .type = MLXSW_SP_RIF_TYPE_VLAN,
3874 .rif_size = sizeof(struct mlxsw_sp_rif),
3875 .configure = mlxsw_sp_rif_vlan_configure,
3876 .deconfigure = mlxsw_sp_rif_vlan_deconfigure,
3877 .fid_get = mlxsw_sp_rif_vlan_fid_get,
3878};
3879
3880static int mlxsw_sp_rif_fid_configure(struct mlxsw_sp_rif *rif)
3881{
3882 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
3883 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
3884 int err;
3885
3886 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index,
3887 true);
3888 if (err)
3889 return err;
3890
0d284818
IS
3891 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
3892 mlxsw_sp_router_port(mlxsw_sp), true);
3893 if (err)
3894 goto err_fid_mc_flood_set;
3895
e4f3c1c1
IS
3896 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
3897 mlxsw_sp_router_port(mlxsw_sp), true);
3898 if (err)
3899 goto err_fid_bc_flood_set;
3900
3901 return 0;
3902
3903err_fid_bc_flood_set:
0d284818
IS
3904 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
3905 mlxsw_sp_router_port(mlxsw_sp), false);
3906err_fid_mc_flood_set:
e4f3c1c1
IS
3907 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
3908 return err;
3909}
3910
3911static void mlxsw_sp_rif_fid_deconfigure(struct mlxsw_sp_rif *rif)
3912{
3913 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
3914 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
3915
3916 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
3917 mlxsw_sp_router_port(mlxsw_sp), false);
0d284818
IS
3918 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
3919 mlxsw_sp_router_port(mlxsw_sp), false);
e4f3c1c1
IS
3920 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
3921}
3922
3923static struct mlxsw_sp_fid *
3924mlxsw_sp_rif_fid_fid_get(struct mlxsw_sp_rif *rif)
3925{
3926 return mlxsw_sp_fid_8021d_get(rif->mlxsw_sp, rif->dev->ifindex);
3927}
3928
3929static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_fid_ops = {
3930 .type = MLXSW_SP_RIF_TYPE_FID,
3931 .rif_size = sizeof(struct mlxsw_sp_rif),
3932 .configure = mlxsw_sp_rif_fid_configure,
3933 .deconfigure = mlxsw_sp_rif_fid_deconfigure,
3934 .fid_get = mlxsw_sp_rif_fid_fid_get,
3935};
3936
3937static const struct mlxsw_sp_rif_ops *mlxsw_sp_rif_ops_arr[] = {
3938 [MLXSW_SP_RIF_TYPE_SUBPORT] = &mlxsw_sp_rif_subport_ops,
3939 [MLXSW_SP_RIF_TYPE_VLAN] = &mlxsw_sp_rif_vlan_ops,
3940 [MLXSW_SP_RIF_TYPE_FID] = &mlxsw_sp_rif_fid_ops,
3941};
3942
348b8fc3
IS
3943static int mlxsw_sp_rifs_init(struct mlxsw_sp *mlxsw_sp)
3944{
3945 u64 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
3946
3947 mlxsw_sp->router->rifs = kcalloc(max_rifs,
3948 sizeof(struct mlxsw_sp_rif *),
3949 GFP_KERNEL);
3950 if (!mlxsw_sp->router->rifs)
3951 return -ENOMEM;
e4f3c1c1
IS
3952
3953 mlxsw_sp->router->rif_ops_arr = mlxsw_sp_rif_ops_arr;
3954
348b8fc3
IS
3955 return 0;
3956}
3957
3958static void mlxsw_sp_rifs_fini(struct mlxsw_sp *mlxsw_sp)
3959{
3960 int i;
3961
3962 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
3963 WARN_ON_ONCE(mlxsw_sp->router->rifs[i]);
3964
3965 kfree(mlxsw_sp->router->rifs);
3966}
3967
c3852ef7
IS
3968static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb)
3969{
7e39d115 3970 struct mlxsw_sp_router *router;
c3852ef7
IS
3971
3972 /* Flush pending FIB notifications and then flush the device's
3973 * table before requesting another dump. The FIB notification
3974 * block is unregistered, so no need to take RTNL.
3975 */
3976 mlxsw_core_flush_owq();
7e39d115
IS
3977 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
3978 mlxsw_sp_router_fib_flush(router->mlxsw_sp);
c3852ef7
IS
3979}
3980
4724ba56
IS
3981static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
3982{
3983 char rgcr_pl[MLXSW_REG_RGCR_LEN];
3984 u64 max_rifs;
3985 int err;
3986
3987 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_RIFS))
3988 return -EIO;
4724ba56 3989 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
4724ba56 3990
e29237e7 3991 mlxsw_reg_rgcr_pack(rgcr_pl, true, true);
4724ba56
IS
3992 mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, max_rifs);
3993 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
3994 if (err)
348b8fc3 3995 return err;
4724ba56 3996 return 0;
4724ba56
IS
3997}
3998
3999static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
4000{
4001 char rgcr_pl[MLXSW_REG_RGCR_LEN];
4724ba56 4002
e29237e7 4003 mlxsw_reg_rgcr_pack(rgcr_pl, false, false);
4724ba56 4004 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
4724ba56
IS
4005}
4006
b45f64d1
JP
4007int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
4008{
9011b677 4009 struct mlxsw_sp_router *router;
b45f64d1
JP
4010 int err;
4011
9011b677
IS
4012 router = kzalloc(sizeof(*mlxsw_sp->router), GFP_KERNEL);
4013 if (!router)
4014 return -ENOMEM;
4015 mlxsw_sp->router = router;
4016 router->mlxsw_sp = mlxsw_sp;
4017
4018 INIT_LIST_HEAD(&mlxsw_sp->router->nexthop_neighs_list);
b45f64d1
JP
4019 err = __mlxsw_sp_router_init(mlxsw_sp);
4020 if (err)
9011b677 4021 goto err_router_init;
b45f64d1 4022
348b8fc3
IS
4023 err = mlxsw_sp_rifs_init(mlxsw_sp);
4024 if (err)
4025 goto err_rifs_init;
4026
9011b677 4027 err = rhashtable_init(&mlxsw_sp->router->nexthop_ht,
c53b8e1b
IS
4028 &mlxsw_sp_nexthop_ht_params);
4029 if (err)
4030 goto err_nexthop_ht_init;
4031
9011b677 4032 err = rhashtable_init(&mlxsw_sp->router->nexthop_group_ht,
e9ad5e7d
IS
4033 &mlxsw_sp_nexthop_group_ht_params);
4034 if (err)
4035 goto err_nexthop_group_ht_init;
4036
8494ab06
IS
4037 err = mlxsw_sp_lpm_init(mlxsw_sp);
4038 if (err)
4039 goto err_lpm_init;
4040
b45f64d1
JP
4041 err = mlxsw_sp_vrs_init(mlxsw_sp);
4042 if (err)
4043 goto err_vrs_init;
4044
8c9583a8 4045 err = mlxsw_sp_neigh_init(mlxsw_sp);
b45f64d1
JP
4046 if (err)
4047 goto err_neigh_init;
4048
7e39d115
IS
4049 mlxsw_sp->router->fib_nb.notifier_call = mlxsw_sp_router_fib_event;
4050 err = register_fib_notifier(&mlxsw_sp->router->fib_nb,
c3852ef7
IS
4051 mlxsw_sp_router_fib_dump_flush);
4052 if (err)
4053 goto err_register_fib_notifier;
4054
b45f64d1
JP
4055 return 0;
4056
c3852ef7
IS
4057err_register_fib_notifier:
4058 mlxsw_sp_neigh_fini(mlxsw_sp);
b45f64d1
JP
4059err_neigh_init:
4060 mlxsw_sp_vrs_fini(mlxsw_sp);
4061err_vrs_init:
8494ab06
IS
4062 mlxsw_sp_lpm_fini(mlxsw_sp);
4063err_lpm_init:
9011b677 4064 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
e9ad5e7d 4065err_nexthop_group_ht_init:
9011b677 4066 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
c53b8e1b 4067err_nexthop_ht_init:
348b8fc3
IS
4068 mlxsw_sp_rifs_fini(mlxsw_sp);
4069err_rifs_init:
b45f64d1 4070 __mlxsw_sp_router_fini(mlxsw_sp);
9011b677
IS
4071err_router_init:
4072 kfree(mlxsw_sp->router);
b45f64d1
JP
4073 return err;
4074}
4075
4076void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
4077{
7e39d115 4078 unregister_fib_notifier(&mlxsw_sp->router->fib_nb);
b45f64d1
JP
4079 mlxsw_sp_neigh_fini(mlxsw_sp);
4080 mlxsw_sp_vrs_fini(mlxsw_sp);
8494ab06 4081 mlxsw_sp_lpm_fini(mlxsw_sp);
9011b677
IS
4082 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
4083 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
348b8fc3 4084 mlxsw_sp_rifs_fini(mlxsw_sp);
b45f64d1 4085 __mlxsw_sp_router_fini(mlxsw_sp);
9011b677 4086 kfree(mlxsw_sp->router);
b45f64d1 4087}