]> git.ipfire.org Git - thirdparty/kernel/stable.git/blame - drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
mlxsw: spectrum_router: Only handle IPv4 and IPv6 events
[thirdparty/kernel/stable.git] / drivers / net / ethernet / mellanox / mlxsw / spectrum_router.c
CommitLineData
464dce18
IS
1/*
2 * drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
6ddb7426 3 * Copyright (c) 2016-2017 Mellanox Technologies. All rights reserved.
464dce18
IS
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>
6ddb7426 7 * Copyright (c) 2017 Petr Machata <petrm@mellanox.com>
464dce18
IS
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the names of the copyright holders nor the names of its
18 * contributors may be used to endorse or promote products derived from
19 * this software without specific prior written permission.
20 *
21 * Alternatively, this software may be distributed under the terms of the
22 * GNU General Public License ("GPL") version 2 as published by the Free
23 * Software Foundation.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
29 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 */
37
38#include <linux/kernel.h>
39#include <linux/types.h>
5e9c16cc
JP
40#include <linux/rhashtable.h>
41#include <linux/bitops.h>
42#include <linux/in6.h>
c723c735 43#include <linux/notifier.h>
df6dd79b 44#include <linux/inetdevice.h>
9db032bb 45#include <linux/netdevice.h>
03ea01e9 46#include <linux/if_bridge.h>
b5f3e0d4 47#include <linux/socket.h>
428b851f 48#include <linux/route.h>
c723c735 49#include <net/netevent.h>
6cf3c971
JP
50#include <net/neighbour.h>
51#include <net/arp.h>
b45f64d1 52#include <net/ip_fib.h>
583419fd 53#include <net/ip6_fib.h>
5d7bfd14 54#include <net/fib_rules.h>
6ddb7426 55#include <net/ip_tunnels.h>
57837885 56#include <net/l3mdev.h>
5ea1237f 57#include <net/addrconf.h>
d5eb89cf
AS
58#include <net/ndisc.h>
59#include <net/ipv6.h>
04b1d4e5 60#include <net/fib_notifier.h>
464dce18
IS
61
62#include "spectrum.h"
63#include "core.h"
64#include "reg.h"
e0c0afd8
AS
65#include "spectrum_cnt.h"
66#include "spectrum_dpipe.h"
38ebc0f4 67#include "spectrum_ipip.h"
e0c0afd8 68#include "spectrum_router.h"
464dce18 69
9011b677
IS
70struct mlxsw_sp_vr;
71struct mlxsw_sp_lpm_tree;
e4f3c1c1 72struct mlxsw_sp_rif_ops;
9011b677
IS
73
74struct mlxsw_sp_router {
75 struct mlxsw_sp *mlxsw_sp;
5f9efffb 76 struct mlxsw_sp_rif **rifs;
9011b677
IS
77 struct mlxsw_sp_vr *vrs;
78 struct rhashtable neigh_ht;
79 struct rhashtable nexthop_group_ht;
80 struct rhashtable nexthop_ht;
81 struct {
82 struct mlxsw_sp_lpm_tree *trees;
83 unsigned int tree_count;
84 } lpm;
85 struct {
86 struct delayed_work dw;
87 unsigned long interval; /* ms */
88 } neighs_update;
89 struct delayed_work nexthop_probe_dw;
90#define MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL 5000 /* ms */
91 struct list_head nexthop_neighs_list;
1012b9ac 92 struct list_head ipip_list;
9011b677 93 bool aborted;
7e39d115 94 struct notifier_block fib_nb;
e4f3c1c1 95 const struct mlxsw_sp_rif_ops **rif_ops_arr;
38ebc0f4 96 const struct mlxsw_sp_ipip_ops **ipip_ops_arr;
9011b677
IS
97};
98
4724ba56
IS
99struct mlxsw_sp_rif {
100 struct list_head nexthop_list;
101 struct list_head neigh_list;
102 struct net_device *dev;
a1107487 103 struct mlxsw_sp_fid *fid;
4724ba56
IS
104 unsigned char addr[ETH_ALEN];
105 int mtu;
bf95233e 106 u16 rif_index;
6913229e 107 u16 vr_id;
e4f3c1c1
IS
108 const struct mlxsw_sp_rif_ops *ops;
109 struct mlxsw_sp *mlxsw_sp;
110
e0c0afd8
AS
111 unsigned int counter_ingress;
112 bool counter_ingress_valid;
113 unsigned int counter_egress;
114 bool counter_egress_valid;
4724ba56
IS
115};
116
e4f3c1c1
IS
117struct mlxsw_sp_rif_params {
118 struct net_device *dev;
119 union {
120 u16 system_port;
121 u16 lag_id;
122 };
123 u16 vid;
124 bool lag;
125};
126
4d93ceeb
IS
127struct mlxsw_sp_rif_subport {
128 struct mlxsw_sp_rif common;
129 union {
130 u16 system_port;
131 u16 lag_id;
132 };
133 u16 vid;
134 bool lag;
135};
136
6ddb7426
PM
137struct mlxsw_sp_rif_ipip_lb {
138 struct mlxsw_sp_rif common;
139 struct mlxsw_sp_rif_ipip_lb_config lb_config;
140 u16 ul_vr_id; /* Reserved for Spectrum-2. */
141};
142
143struct mlxsw_sp_rif_params_ipip_lb {
144 struct mlxsw_sp_rif_params common;
145 struct mlxsw_sp_rif_ipip_lb_config lb_config;
146};
147
e4f3c1c1
IS
148struct mlxsw_sp_rif_ops {
149 enum mlxsw_sp_rif_type type;
150 size_t rif_size;
151
152 void (*setup)(struct mlxsw_sp_rif *rif,
153 const struct mlxsw_sp_rif_params *params);
154 int (*configure)(struct mlxsw_sp_rif *rif);
155 void (*deconfigure)(struct mlxsw_sp_rif *rif);
156 struct mlxsw_sp_fid * (*fid_get)(struct mlxsw_sp_rif *rif);
157};
158
e0c0afd8
AS
159static unsigned int *
160mlxsw_sp_rif_p_counter_get(struct mlxsw_sp_rif *rif,
161 enum mlxsw_sp_rif_counter_dir dir)
162{
163 switch (dir) {
164 case MLXSW_SP_RIF_COUNTER_EGRESS:
165 return &rif->counter_egress;
166 case MLXSW_SP_RIF_COUNTER_INGRESS:
167 return &rif->counter_ingress;
168 }
169 return NULL;
170}
171
172static bool
173mlxsw_sp_rif_counter_valid_get(struct mlxsw_sp_rif *rif,
174 enum mlxsw_sp_rif_counter_dir dir)
175{
176 switch (dir) {
177 case MLXSW_SP_RIF_COUNTER_EGRESS:
178 return rif->counter_egress_valid;
179 case MLXSW_SP_RIF_COUNTER_INGRESS:
180 return rif->counter_ingress_valid;
181 }
182 return false;
183}
184
185static void
186mlxsw_sp_rif_counter_valid_set(struct mlxsw_sp_rif *rif,
187 enum mlxsw_sp_rif_counter_dir dir,
188 bool valid)
189{
190 switch (dir) {
191 case MLXSW_SP_RIF_COUNTER_EGRESS:
192 rif->counter_egress_valid = valid;
193 break;
194 case MLXSW_SP_RIF_COUNTER_INGRESS:
195 rif->counter_ingress_valid = valid;
196 break;
197 }
198}
199
200static int mlxsw_sp_rif_counter_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
201 unsigned int counter_index, bool enable,
202 enum mlxsw_sp_rif_counter_dir dir)
203{
204 char ritr_pl[MLXSW_REG_RITR_LEN];
205 bool is_egress = false;
206 int err;
207
208 if (dir == MLXSW_SP_RIF_COUNTER_EGRESS)
209 is_egress = true;
210 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
211 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
212 if (err)
213 return err;
214
215 mlxsw_reg_ritr_counter_pack(ritr_pl, counter_index, enable,
216 is_egress);
217 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
218}
219
220int mlxsw_sp_rif_counter_value_get(struct mlxsw_sp *mlxsw_sp,
221 struct mlxsw_sp_rif *rif,
222 enum mlxsw_sp_rif_counter_dir dir, u64 *cnt)
223{
224 char ricnt_pl[MLXSW_REG_RICNT_LEN];
225 unsigned int *p_counter_index;
226 bool valid;
227 int err;
228
229 valid = mlxsw_sp_rif_counter_valid_get(rif, dir);
230 if (!valid)
231 return -EINVAL;
232
233 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
234 if (!p_counter_index)
235 return -EINVAL;
236 mlxsw_reg_ricnt_pack(ricnt_pl, *p_counter_index,
237 MLXSW_REG_RICNT_OPCODE_NOP);
238 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
239 if (err)
240 return err;
241 *cnt = mlxsw_reg_ricnt_good_unicast_packets_get(ricnt_pl);
242 return 0;
243}
244
245static int mlxsw_sp_rif_counter_clear(struct mlxsw_sp *mlxsw_sp,
246 unsigned int counter_index)
247{
248 char ricnt_pl[MLXSW_REG_RICNT_LEN];
249
250 mlxsw_reg_ricnt_pack(ricnt_pl, counter_index,
251 MLXSW_REG_RICNT_OPCODE_CLEAR);
252 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
253}
254
255int mlxsw_sp_rif_counter_alloc(struct mlxsw_sp *mlxsw_sp,
256 struct mlxsw_sp_rif *rif,
257 enum mlxsw_sp_rif_counter_dir dir)
258{
259 unsigned int *p_counter_index;
260 int err;
261
262 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
263 if (!p_counter_index)
264 return -EINVAL;
265 err = mlxsw_sp_counter_alloc(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
266 p_counter_index);
267 if (err)
268 return err;
269
270 err = mlxsw_sp_rif_counter_clear(mlxsw_sp, *p_counter_index);
271 if (err)
272 goto err_counter_clear;
273
274 err = mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
275 *p_counter_index, true, dir);
276 if (err)
277 goto err_counter_edit;
278 mlxsw_sp_rif_counter_valid_set(rif, dir, true);
279 return 0;
280
281err_counter_edit:
282err_counter_clear:
283 mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
284 *p_counter_index);
285 return err;
286}
287
288void mlxsw_sp_rif_counter_free(struct mlxsw_sp *mlxsw_sp,
289 struct mlxsw_sp_rif *rif,
290 enum mlxsw_sp_rif_counter_dir dir)
291{
292 unsigned int *p_counter_index;
293
6b1206bb
AS
294 if (!mlxsw_sp_rif_counter_valid_get(rif, dir))
295 return;
296
e0c0afd8
AS
297 p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
298 if (WARN_ON(!p_counter_index))
299 return;
300 mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
301 *p_counter_index, false, dir);
302 mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
303 *p_counter_index);
304 mlxsw_sp_rif_counter_valid_set(rif, dir, false);
305}
306
e4f3c1c1
IS
307static void mlxsw_sp_rif_counters_alloc(struct mlxsw_sp_rif *rif)
308{
309 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
310 struct devlink *devlink;
311
312 devlink = priv_to_devlink(mlxsw_sp->core);
313 if (!devlink_dpipe_table_counter_enabled(devlink,
314 MLXSW_SP_DPIPE_TABLE_NAME_ERIF))
315 return;
316 mlxsw_sp_rif_counter_alloc(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS);
317}
318
319static void mlxsw_sp_rif_counters_free(struct mlxsw_sp_rif *rif)
320{
321 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
322
323 mlxsw_sp_rif_counter_free(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS);
324}
325
4724ba56
IS
326static struct mlxsw_sp_rif *
327mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
328 const struct net_device *dev);
329
7dcc18ad 330#define MLXSW_SP_PREFIX_COUNT (sizeof(struct in6_addr) * BITS_PER_BYTE + 1)
9011b677
IS
331
332struct mlxsw_sp_prefix_usage {
333 DECLARE_BITMAP(b, MLXSW_SP_PREFIX_COUNT);
334};
335
53342023
JP
336#define mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) \
337 for_each_set_bit(prefix, (prefix_usage)->b, MLXSW_SP_PREFIX_COUNT)
338
339static bool
340mlxsw_sp_prefix_usage_eq(struct mlxsw_sp_prefix_usage *prefix_usage1,
341 struct mlxsw_sp_prefix_usage *prefix_usage2)
342{
343 return !memcmp(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
344}
345
6b75c480
JP
346static bool
347mlxsw_sp_prefix_usage_none(struct mlxsw_sp_prefix_usage *prefix_usage)
348{
349 struct mlxsw_sp_prefix_usage prefix_usage_none = {{ 0 } };
350
351 return mlxsw_sp_prefix_usage_eq(prefix_usage, &prefix_usage_none);
352}
353
354static void
355mlxsw_sp_prefix_usage_cpy(struct mlxsw_sp_prefix_usage *prefix_usage1,
356 struct mlxsw_sp_prefix_usage *prefix_usage2)
357{
358 memcpy(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
359}
360
5e9c16cc
JP
361static void
362mlxsw_sp_prefix_usage_set(struct mlxsw_sp_prefix_usage *prefix_usage,
363 unsigned char prefix_len)
364{
365 set_bit(prefix_len, prefix_usage->b);
366}
367
368static void
369mlxsw_sp_prefix_usage_clear(struct mlxsw_sp_prefix_usage *prefix_usage,
370 unsigned char prefix_len)
371{
372 clear_bit(prefix_len, prefix_usage->b);
373}
374
375struct mlxsw_sp_fib_key {
376 unsigned char addr[sizeof(struct in6_addr)];
377 unsigned char prefix_len;
378};
379
61c503f9
JP
380enum mlxsw_sp_fib_entry_type {
381 MLXSW_SP_FIB_ENTRY_TYPE_REMOTE,
382 MLXSW_SP_FIB_ENTRY_TYPE_LOCAL,
383 MLXSW_SP_FIB_ENTRY_TYPE_TRAP,
4607f6d2
PM
384
385 /* This is a special case of local delivery, where a packet should be
386 * decapsulated on reception. Note that there is no corresponding ENCAP,
387 * because that's a type of next hop, not of FIB entry. (There can be
388 * several next hops in a REMOTE entry, and some of them may be
389 * encapsulating entries.)
390 */
391 MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP,
61c503f9
JP
392};
393
a7ff87ac 394struct mlxsw_sp_nexthop_group;
9011b677 395struct mlxsw_sp_fib;
a7ff87ac 396
9aecce1c
IS
397struct mlxsw_sp_fib_node {
398 struct list_head entry_list;
b45f64d1 399 struct list_head list;
9aecce1c 400 struct rhash_head ht_node;
76610ebb 401 struct mlxsw_sp_fib *fib;
5e9c16cc 402 struct mlxsw_sp_fib_key key;
9aecce1c
IS
403};
404
4607f6d2
PM
405struct mlxsw_sp_fib_entry_decap {
406 struct mlxsw_sp_ipip_entry *ipip_entry;
407 u32 tunnel_index;
408};
409
9aecce1c
IS
410struct mlxsw_sp_fib_entry {
411 struct list_head list;
412 struct mlxsw_sp_fib_node *fib_node;
61c503f9 413 enum mlxsw_sp_fib_entry_type type;
a7ff87ac
JP
414 struct list_head nexthop_group_node;
415 struct mlxsw_sp_nexthop_group *nh_group;
4607f6d2 416 struct mlxsw_sp_fib_entry_decap decap; /* Valid for decap entries. */
5e9c16cc
JP
417};
418
4f1c7f1f
IS
419struct mlxsw_sp_fib4_entry {
420 struct mlxsw_sp_fib_entry common;
421 u32 tb_id;
422 u32 prio;
423 u8 tos;
424 u8 type;
425};
426
428b851f
IS
427struct mlxsw_sp_fib6_entry {
428 struct mlxsw_sp_fib_entry common;
429 struct list_head rt6_list;
430 unsigned int nrt6;
431};
432
433struct mlxsw_sp_rt6 {
434 struct list_head list;
435 struct rt6_info *rt;
436};
437
9011b677
IS
438struct mlxsw_sp_lpm_tree {
439 u8 id; /* tree ID */
440 unsigned int ref_count;
441 enum mlxsw_sp_l3proto proto;
442 struct mlxsw_sp_prefix_usage prefix_usage;
443};
444
5e9c16cc
JP
445struct mlxsw_sp_fib {
446 struct rhashtable ht;
9aecce1c 447 struct list_head node_list;
76610ebb
IS
448 struct mlxsw_sp_vr *vr;
449 struct mlxsw_sp_lpm_tree *lpm_tree;
5e9c16cc
JP
450 unsigned long prefix_ref_count[MLXSW_SP_PREFIX_COUNT];
451 struct mlxsw_sp_prefix_usage prefix_usage;
76610ebb 452 enum mlxsw_sp_l3proto proto;
5e9c16cc
JP
453};
454
9011b677
IS
455struct mlxsw_sp_vr {
456 u16 id; /* virtual router ID */
457 u32 tb_id; /* kernel fib table id */
458 unsigned int rif_count;
459 struct mlxsw_sp_fib *fib4;
a3d9bc50 460 struct mlxsw_sp_fib *fib6;
9011b677
IS
461};
462
9aecce1c 463static const struct rhashtable_params mlxsw_sp_fib_ht_params;
5e9c16cc 464
76610ebb
IS
465static struct mlxsw_sp_fib *mlxsw_sp_fib_create(struct mlxsw_sp_vr *vr,
466 enum mlxsw_sp_l3proto proto)
5e9c16cc
JP
467{
468 struct mlxsw_sp_fib *fib;
469 int err;
470
471 fib = kzalloc(sizeof(*fib), GFP_KERNEL);
472 if (!fib)
473 return ERR_PTR(-ENOMEM);
474 err = rhashtable_init(&fib->ht, &mlxsw_sp_fib_ht_params);
475 if (err)
476 goto err_rhashtable_init;
9aecce1c 477 INIT_LIST_HEAD(&fib->node_list);
76610ebb
IS
478 fib->proto = proto;
479 fib->vr = vr;
5e9c16cc
JP
480 return fib;
481
482err_rhashtable_init:
483 kfree(fib);
484 return ERR_PTR(err);
485}
486
487static void mlxsw_sp_fib_destroy(struct mlxsw_sp_fib *fib)
488{
9aecce1c 489 WARN_ON(!list_empty(&fib->node_list));
76610ebb 490 WARN_ON(fib->lpm_tree);
5e9c16cc
JP
491 rhashtable_destroy(&fib->ht);
492 kfree(fib);
493}
494
53342023 495static struct mlxsw_sp_lpm_tree *
382dbb40 496mlxsw_sp_lpm_tree_find_unused(struct mlxsw_sp *mlxsw_sp)
53342023
JP
497{
498 static struct mlxsw_sp_lpm_tree *lpm_tree;
499 int i;
500
9011b677
IS
501 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
502 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
382dbb40
IS
503 if (lpm_tree->ref_count == 0)
504 return lpm_tree;
53342023
JP
505 }
506 return NULL;
507}
508
509static int mlxsw_sp_lpm_tree_alloc(struct mlxsw_sp *mlxsw_sp,
510 struct mlxsw_sp_lpm_tree *lpm_tree)
511{
512 char ralta_pl[MLXSW_REG_RALTA_LEN];
513
1a9234e6
IS
514 mlxsw_reg_ralta_pack(ralta_pl, true,
515 (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
516 lpm_tree->id);
53342023
JP
517 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
518}
519
cc702670
IS
520static void mlxsw_sp_lpm_tree_free(struct mlxsw_sp *mlxsw_sp,
521 struct mlxsw_sp_lpm_tree *lpm_tree)
53342023
JP
522{
523 char ralta_pl[MLXSW_REG_RALTA_LEN];
524
1a9234e6
IS
525 mlxsw_reg_ralta_pack(ralta_pl, false,
526 (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
527 lpm_tree->id);
cc702670 528 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
53342023
JP
529}
530
531static int
532mlxsw_sp_lpm_tree_left_struct_set(struct mlxsw_sp *mlxsw_sp,
533 struct mlxsw_sp_prefix_usage *prefix_usage,
534 struct mlxsw_sp_lpm_tree *lpm_tree)
535{
536 char ralst_pl[MLXSW_REG_RALST_LEN];
537 u8 root_bin = 0;
538 u8 prefix;
539 u8 last_prefix = MLXSW_REG_RALST_BIN_NO_CHILD;
540
541 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage)
542 root_bin = prefix;
543
544 mlxsw_reg_ralst_pack(ralst_pl, root_bin, lpm_tree->id);
545 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) {
546 if (prefix == 0)
547 continue;
548 mlxsw_reg_ralst_bin_pack(ralst_pl, prefix, last_prefix,
549 MLXSW_REG_RALST_BIN_NO_CHILD);
550 last_prefix = prefix;
551 }
552 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
553}
554
555static struct mlxsw_sp_lpm_tree *
556mlxsw_sp_lpm_tree_create(struct mlxsw_sp *mlxsw_sp,
557 struct mlxsw_sp_prefix_usage *prefix_usage,
382dbb40 558 enum mlxsw_sp_l3proto proto)
53342023
JP
559{
560 struct mlxsw_sp_lpm_tree *lpm_tree;
561 int err;
562
382dbb40 563 lpm_tree = mlxsw_sp_lpm_tree_find_unused(mlxsw_sp);
53342023
JP
564 if (!lpm_tree)
565 return ERR_PTR(-EBUSY);
566 lpm_tree->proto = proto;
567 err = mlxsw_sp_lpm_tree_alloc(mlxsw_sp, lpm_tree);
568 if (err)
569 return ERR_PTR(err);
570
571 err = mlxsw_sp_lpm_tree_left_struct_set(mlxsw_sp, prefix_usage,
572 lpm_tree);
573 if (err)
574 goto err_left_struct_set;
2083d367
JP
575 memcpy(&lpm_tree->prefix_usage, prefix_usage,
576 sizeof(lpm_tree->prefix_usage));
53342023
JP
577 return lpm_tree;
578
579err_left_struct_set:
580 mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
581 return ERR_PTR(err);
582}
583
cc702670
IS
584static void mlxsw_sp_lpm_tree_destroy(struct mlxsw_sp *mlxsw_sp,
585 struct mlxsw_sp_lpm_tree *lpm_tree)
53342023 586{
cc702670 587 mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
53342023
JP
588}
589
590static struct mlxsw_sp_lpm_tree *
591mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp,
592 struct mlxsw_sp_prefix_usage *prefix_usage,
382dbb40 593 enum mlxsw_sp_l3proto proto)
53342023
JP
594{
595 struct mlxsw_sp_lpm_tree *lpm_tree;
596 int i;
597
9011b677
IS
598 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
599 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
8b99becd
JP
600 if (lpm_tree->ref_count != 0 &&
601 lpm_tree->proto == proto &&
53342023
JP
602 mlxsw_sp_prefix_usage_eq(&lpm_tree->prefix_usage,
603 prefix_usage))
fc922bb0 604 return lpm_tree;
53342023 605 }
fc922bb0
IS
606 return mlxsw_sp_lpm_tree_create(mlxsw_sp, prefix_usage, proto);
607}
53342023 608
fc922bb0
IS
609static void mlxsw_sp_lpm_tree_hold(struct mlxsw_sp_lpm_tree *lpm_tree)
610{
53342023 611 lpm_tree->ref_count++;
53342023
JP
612}
613
cc702670
IS
614static void mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp,
615 struct mlxsw_sp_lpm_tree *lpm_tree)
53342023
JP
616{
617 if (--lpm_tree->ref_count == 0)
cc702670 618 mlxsw_sp_lpm_tree_destroy(mlxsw_sp, lpm_tree);
53342023
JP
619}
620
d7a60306 621#define MLXSW_SP_LPM_TREE_MIN 1 /* tree 0 is reserved */
8494ab06
IS
622
623static int mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp)
53342023
JP
624{
625 struct mlxsw_sp_lpm_tree *lpm_tree;
8494ab06 626 u64 max_trees;
53342023
JP
627 int i;
628
8494ab06
IS
629 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_LPM_TREES))
630 return -EIO;
631
632 max_trees = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_LPM_TREES);
9011b677
IS
633 mlxsw_sp->router->lpm.tree_count = max_trees - MLXSW_SP_LPM_TREE_MIN;
634 mlxsw_sp->router->lpm.trees = kcalloc(mlxsw_sp->router->lpm.tree_count,
8494ab06
IS
635 sizeof(struct mlxsw_sp_lpm_tree),
636 GFP_KERNEL);
9011b677 637 if (!mlxsw_sp->router->lpm.trees)
8494ab06
IS
638 return -ENOMEM;
639
9011b677
IS
640 for (i = 0; i < mlxsw_sp->router->lpm.tree_count; i++) {
641 lpm_tree = &mlxsw_sp->router->lpm.trees[i];
53342023
JP
642 lpm_tree->id = i + MLXSW_SP_LPM_TREE_MIN;
643 }
8494ab06
IS
644
645 return 0;
646}
647
648static void mlxsw_sp_lpm_fini(struct mlxsw_sp *mlxsw_sp)
649{
9011b677 650 kfree(mlxsw_sp->router->lpm.trees);
53342023
JP
651}
652
76610ebb
IS
653static bool mlxsw_sp_vr_is_used(const struct mlxsw_sp_vr *vr)
654{
a3d9bc50 655 return !!vr->fib4 || !!vr->fib6;
76610ebb
IS
656}
657
6b75c480
JP
658static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp)
659{
660 struct mlxsw_sp_vr *vr;
661 int i;
662
c1a38311 663 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
9011b677 664 vr = &mlxsw_sp->router->vrs[i];
76610ebb 665 if (!mlxsw_sp_vr_is_used(vr))
6b75c480
JP
666 return vr;
667 }
668 return NULL;
669}
670
671static int mlxsw_sp_vr_lpm_tree_bind(struct mlxsw_sp *mlxsw_sp,
0adb214b 672 const struct mlxsw_sp_fib *fib, u8 tree_id)
6b75c480
JP
673{
674 char raltb_pl[MLXSW_REG_RALTB_LEN];
675
76610ebb
IS
676 mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,
677 (enum mlxsw_reg_ralxx_protocol) fib->proto,
0adb214b 678 tree_id);
6b75c480
JP
679 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
680}
681
682static int mlxsw_sp_vr_lpm_tree_unbind(struct mlxsw_sp *mlxsw_sp,
76610ebb 683 const struct mlxsw_sp_fib *fib)
6b75c480
JP
684{
685 char raltb_pl[MLXSW_REG_RALTB_LEN];
686
687 /* Bind to tree 0 which is default */
76610ebb
IS
688 mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,
689 (enum mlxsw_reg_ralxx_protocol) fib->proto, 0);
6b75c480
JP
690 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
691}
692
693static u32 mlxsw_sp_fix_tb_id(u32 tb_id)
694{
695 /* For our purpose, squash main and local table into one */
696 if (tb_id == RT_TABLE_LOCAL)
697 tb_id = RT_TABLE_MAIN;
698 return tb_id;
699}
700
701static struct mlxsw_sp_vr *mlxsw_sp_vr_find(struct mlxsw_sp *mlxsw_sp,
76610ebb 702 u32 tb_id)
6b75c480
JP
703{
704 struct mlxsw_sp_vr *vr;
705 int i;
706
707 tb_id = mlxsw_sp_fix_tb_id(tb_id);
9497c042 708
c1a38311 709 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
9011b677 710 vr = &mlxsw_sp->router->vrs[i];
76610ebb 711 if (mlxsw_sp_vr_is_used(vr) && vr->tb_id == tb_id)
6b75c480
JP
712 return vr;
713 }
714 return NULL;
715}
716
76610ebb
IS
717static struct mlxsw_sp_fib *mlxsw_sp_vr_fib(const struct mlxsw_sp_vr *vr,
718 enum mlxsw_sp_l3proto proto)
719{
720 switch (proto) {
721 case MLXSW_SP_L3_PROTO_IPV4:
722 return vr->fib4;
723 case MLXSW_SP_L3_PROTO_IPV6:
a3d9bc50 724 return vr->fib6;
76610ebb
IS
725 }
726 return NULL;
727}
728
6b75c480 729static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp,
76610ebb 730 u32 tb_id)
6b75c480 731{
6b75c480 732 struct mlxsw_sp_vr *vr;
a3d9bc50 733 int err;
6b75c480
JP
734
735 vr = mlxsw_sp_vr_find_unused(mlxsw_sp);
736 if (!vr)
737 return ERR_PTR(-EBUSY);
76610ebb
IS
738 vr->fib4 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV4);
739 if (IS_ERR(vr->fib4))
740 return ERR_CAST(vr->fib4);
a3d9bc50
IS
741 vr->fib6 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV6);
742 if (IS_ERR(vr->fib6)) {
743 err = PTR_ERR(vr->fib6);
744 goto err_fib6_create;
745 }
6b75c480 746 vr->tb_id = tb_id;
6b75c480 747 return vr;
a3d9bc50
IS
748
749err_fib6_create:
750 mlxsw_sp_fib_destroy(vr->fib4);
751 vr->fib4 = NULL;
752 return ERR_PTR(err);
6b75c480
JP
753}
754
76610ebb 755static void mlxsw_sp_vr_destroy(struct mlxsw_sp_vr *vr)
6b75c480 756{
a3d9bc50
IS
757 mlxsw_sp_fib_destroy(vr->fib6);
758 vr->fib6 = NULL;
76610ebb
IS
759 mlxsw_sp_fib_destroy(vr->fib4);
760 vr->fib4 = NULL;
6b75c480
JP
761}
762
76610ebb 763static struct mlxsw_sp_vr *mlxsw_sp_vr_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id)
6b75c480
JP
764{
765 struct mlxsw_sp_vr *vr;
6b75c480
JP
766
767 tb_id = mlxsw_sp_fix_tb_id(tb_id);
76610ebb
IS
768 vr = mlxsw_sp_vr_find(mlxsw_sp, tb_id);
769 if (!vr)
770 vr = mlxsw_sp_vr_create(mlxsw_sp, tb_id);
6b75c480
JP
771 return vr;
772}
773
76610ebb 774static void mlxsw_sp_vr_put(struct mlxsw_sp_vr *vr)
6b75c480 775{
a3d9bc50
IS
776 if (!vr->rif_count && list_empty(&vr->fib4->node_list) &&
777 list_empty(&vr->fib6->node_list))
76610ebb 778 mlxsw_sp_vr_destroy(vr);
6b75c480
JP
779}
780
fc922bb0
IS
781static bool
782mlxsw_sp_vr_lpm_tree_should_replace(struct mlxsw_sp_vr *vr,
783 enum mlxsw_sp_l3proto proto, u8 tree_id)
784{
785 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
786
787 if (!mlxsw_sp_vr_is_used(vr))
788 return false;
789 if (fib->lpm_tree && fib->lpm_tree->id == tree_id)
790 return true;
791 return false;
792}
793
794static int mlxsw_sp_vr_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp,
795 struct mlxsw_sp_fib *fib,
796 struct mlxsw_sp_lpm_tree *new_tree)
797{
798 struct mlxsw_sp_lpm_tree *old_tree = fib->lpm_tree;
799 int err;
800
801 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, new_tree->id);
802 if (err)
803 return err;
804 fib->lpm_tree = new_tree;
805 mlxsw_sp_lpm_tree_hold(new_tree);
806 mlxsw_sp_lpm_tree_put(mlxsw_sp, old_tree);
807 return 0;
808}
809
810static int mlxsw_sp_vrs_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp,
811 struct mlxsw_sp_fib *fib,
812 struct mlxsw_sp_lpm_tree *new_tree)
813{
814 struct mlxsw_sp_lpm_tree *old_tree = fib->lpm_tree;
815 enum mlxsw_sp_l3proto proto = fib->proto;
816 u8 old_id, new_id = new_tree->id;
817 struct mlxsw_sp_vr *vr;
818 int i, err;
819
820 if (!old_tree)
821 goto no_replace;
822 old_id = old_tree->id;
823
824 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
825 vr = &mlxsw_sp->router->vrs[i];
826 if (!mlxsw_sp_vr_lpm_tree_should_replace(vr, proto, old_id))
827 continue;
828 err = mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp,
829 mlxsw_sp_vr_fib(vr, proto),
830 new_tree);
831 if (err)
832 goto err_tree_replace;
833 }
834
835 return 0;
836
837err_tree_replace:
838 for (i--; i >= 0; i--) {
839 if (!mlxsw_sp_vr_lpm_tree_should_replace(vr, proto, new_id))
840 continue;
841 mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp,
842 mlxsw_sp_vr_fib(vr, proto),
843 old_tree);
844 }
845 return err;
846
847no_replace:
848 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, new_tree->id);
849 if (err)
850 return err;
851 fib->lpm_tree = new_tree;
852 mlxsw_sp_lpm_tree_hold(new_tree);
853 return 0;
854}
855
856static void
857mlxsw_sp_vrs_prefixes(struct mlxsw_sp *mlxsw_sp,
858 enum mlxsw_sp_l3proto proto,
859 struct mlxsw_sp_prefix_usage *req_prefix_usage)
860{
861 int i;
862
863 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
864 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
865 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
866 unsigned char prefix;
867
868 if (!mlxsw_sp_vr_is_used(vr))
869 continue;
870 mlxsw_sp_prefix_usage_for_each(prefix, &fib->prefix_usage)
871 mlxsw_sp_prefix_usage_set(req_prefix_usage, prefix);
872 }
873}
874
9497c042 875static int mlxsw_sp_vrs_init(struct mlxsw_sp *mlxsw_sp)
6b75c480
JP
876{
877 struct mlxsw_sp_vr *vr;
c1a38311 878 u64 max_vrs;
6b75c480
JP
879 int i;
880
c1a38311 881 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_VRS))
9497c042
NF
882 return -EIO;
883
c1a38311 884 max_vrs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS);
9011b677
IS
885 mlxsw_sp->router->vrs = kcalloc(max_vrs, sizeof(struct mlxsw_sp_vr),
886 GFP_KERNEL);
887 if (!mlxsw_sp->router->vrs)
9497c042
NF
888 return -ENOMEM;
889
c1a38311 890 for (i = 0; i < max_vrs; i++) {
9011b677 891 vr = &mlxsw_sp->router->vrs[i];
6b75c480
JP
892 vr->id = i;
893 }
9497c042
NF
894
895 return 0;
896}
897
ac571de9
IS
898static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp);
899
9497c042
NF
900static void mlxsw_sp_vrs_fini(struct mlxsw_sp *mlxsw_sp)
901{
3057224e
IS
902 /* At this stage we're guaranteed not to have new incoming
903 * FIB notifications and the work queue is free from FIBs
904 * sitting on top of mlxsw netdevs. However, we can still
905 * have other FIBs queued. Flush the queue before flushing
906 * the device's tables. No need for locks, as we're the only
907 * writer.
908 */
909 mlxsw_core_flush_owq();
ac571de9 910 mlxsw_sp_router_fib_flush(mlxsw_sp);
9011b677 911 kfree(mlxsw_sp->router->vrs);
6b75c480
JP
912}
913
6ddb7426
PM
914static struct net_device *
915__mlxsw_sp_ipip_netdev_ul_dev_get(const struct net_device *ol_dev)
916{
917 struct ip_tunnel *tun = netdev_priv(ol_dev);
918 struct net *net = dev_net(ol_dev);
919
920 return __dev_get_by_index(net, tun->parms.link);
921}
922
923static u32 mlxsw_sp_ipip_dev_ul_tb_id(const struct net_device *ol_dev)
924{
925 struct net_device *d = __mlxsw_sp_ipip_netdev_ul_dev_get(ol_dev);
926
927 if (d)
928 return l3mdev_fib_table(d) ? : RT_TABLE_MAIN;
929 else
930 return l3mdev_fib_table(ol_dev) ? : RT_TABLE_MAIN;
931}
932
1012b9ac
PM
933static struct mlxsw_sp_rif *
934mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
935 const struct mlxsw_sp_rif_params *params);
936
937static struct mlxsw_sp_rif_ipip_lb *
938mlxsw_sp_ipip_ol_ipip_lb_create(struct mlxsw_sp *mlxsw_sp,
939 enum mlxsw_sp_ipip_type ipipt,
940 struct net_device *ol_dev)
941{
942 struct mlxsw_sp_rif_params_ipip_lb lb_params;
943 const struct mlxsw_sp_ipip_ops *ipip_ops;
944 struct mlxsw_sp_rif *rif;
945
946 ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipipt];
947 lb_params = (struct mlxsw_sp_rif_params_ipip_lb) {
948 .common.dev = ol_dev,
949 .common.lag = false,
950 .lb_config = ipip_ops->ol_loopback_config(mlxsw_sp, ol_dev),
951 };
952
953 rif = mlxsw_sp_rif_create(mlxsw_sp, &lb_params.common);
954 if (IS_ERR(rif))
955 return ERR_CAST(rif);
956 return container_of(rif, struct mlxsw_sp_rif_ipip_lb, common);
957}
958
959static struct mlxsw_sp_ipip_entry *
960mlxsw_sp_ipip_entry_alloc(struct mlxsw_sp *mlxsw_sp,
961 enum mlxsw_sp_ipip_type ipipt,
962 struct net_device *ol_dev)
963{
964 struct mlxsw_sp_ipip_entry *ipip_entry;
965 struct mlxsw_sp_ipip_entry *ret = NULL;
966
967 ipip_entry = kzalloc(sizeof(*ipip_entry), GFP_KERNEL);
968 if (!ipip_entry)
969 return ERR_PTR(-ENOMEM);
970
971 ipip_entry->ol_lb = mlxsw_sp_ipip_ol_ipip_lb_create(mlxsw_sp, ipipt,
972 ol_dev);
973 if (IS_ERR(ipip_entry->ol_lb)) {
974 ret = ERR_CAST(ipip_entry->ol_lb);
975 goto err_ol_ipip_lb_create;
976 }
977
978 ipip_entry->ipipt = ipipt;
979 ipip_entry->ol_dev = ol_dev;
980
981 return ipip_entry;
982
983err_ol_ipip_lb_create:
984 kfree(ipip_entry);
985 return ret;
986}
987
988static void
989mlxsw_sp_ipip_entry_destroy(struct mlxsw_sp_ipip_entry *ipip_entry)
990{
991 WARN_ON(ipip_entry->ref_count > 0);
992 mlxsw_sp_rif_destroy(&ipip_entry->ol_lb->common);
993 kfree(ipip_entry);
994}
995
996static __be32
997mlxsw_sp_ipip_netdev_saddr4(const struct net_device *ol_dev)
998{
999 struct ip_tunnel *tun = netdev_priv(ol_dev);
1000
1001 return tun->parms.iph.saddr;
1002}
1003
1004union mlxsw_sp_l3addr
1005mlxsw_sp_ipip_netdev_saddr(enum mlxsw_sp_l3proto proto,
1006 const struct net_device *ol_dev)
1007{
1008 switch (proto) {
1009 case MLXSW_SP_L3_PROTO_IPV4:
1010 return (union mlxsw_sp_l3addr) {
1011 .addr4 = mlxsw_sp_ipip_netdev_saddr4(ol_dev),
1012 };
1013 case MLXSW_SP_L3_PROTO_IPV6:
1014 break;
1015 };
1016
1017 WARN_ON(1);
1018 return (union mlxsw_sp_l3addr) {
1019 .addr4 = 0,
1020 };
1021}
1022
ee954d1a
PM
1023__be32 mlxsw_sp_ipip_netdev_daddr4(const struct net_device *ol_dev)
1024{
1025 struct ip_tunnel *tun = netdev_priv(ol_dev);
1026
1027 return tun->parms.iph.daddr;
1028}
1029
1030union mlxsw_sp_l3addr
1031mlxsw_sp_ipip_netdev_daddr(enum mlxsw_sp_l3proto proto,
1032 const struct net_device *ol_dev)
1033{
1034 switch (proto) {
1035 case MLXSW_SP_L3_PROTO_IPV4:
1036 return (union mlxsw_sp_l3addr) {
1037 .addr4 = mlxsw_sp_ipip_netdev_daddr4(ol_dev),
1038 };
1039 case MLXSW_SP_L3_PROTO_IPV6:
1040 break;
1041 };
1042
1043 WARN_ON(1);
1044 return (union mlxsw_sp_l3addr) {
1045 .addr4 = 0,
1046 };
1047}
1048
1012b9ac
PM
1049static bool mlxsw_sp_l3addr_eq(const union mlxsw_sp_l3addr *addr1,
1050 const union mlxsw_sp_l3addr *addr2)
1051{
1052 return !memcmp(addr1, addr2, sizeof(*addr1));
1053}
1054
1055static bool
1056mlxsw_sp_ipip_entry_saddr_matches(struct mlxsw_sp *mlxsw_sp,
1057 const enum mlxsw_sp_l3proto ul_proto,
1058 union mlxsw_sp_l3addr saddr,
1059 u32 ul_tb_id,
1060 struct mlxsw_sp_ipip_entry *ipip_entry)
1061{
1062 u32 tun_ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ipip_entry->ol_dev);
1063 enum mlxsw_sp_ipip_type ipipt = ipip_entry->ipipt;
1064 union mlxsw_sp_l3addr tun_saddr;
1065
1066 if (mlxsw_sp->router->ipip_ops_arr[ipipt]->ul_proto != ul_proto)
1067 return false;
1068
1069 tun_saddr = mlxsw_sp_ipip_netdev_saddr(ul_proto, ipip_entry->ol_dev);
1070 return tun_ul_tb_id == ul_tb_id &&
1071 mlxsw_sp_l3addr_eq(&tun_saddr, &saddr);
1072}
1073
4607f6d2
PM
1074static int
1075mlxsw_sp_fib_entry_decap_init(struct mlxsw_sp *mlxsw_sp,
1076 struct mlxsw_sp_fib_entry *fib_entry,
1077 struct mlxsw_sp_ipip_entry *ipip_entry)
1078{
1079 u32 tunnel_index;
1080 int err;
1081
1082 err = mlxsw_sp_kvdl_alloc(mlxsw_sp, 1, &tunnel_index);
1083 if (err)
1084 return err;
1085
1086 ipip_entry->decap_fib_entry = fib_entry;
1087 fib_entry->decap.ipip_entry = ipip_entry;
1088 fib_entry->decap.tunnel_index = tunnel_index;
1089 return 0;
1090}
1091
1092static void mlxsw_sp_fib_entry_decap_fini(struct mlxsw_sp *mlxsw_sp,
1093 struct mlxsw_sp_fib_entry *fib_entry)
1094{
1095 /* Unlink this node from the IPIP entry that it's the decap entry of. */
1096 fib_entry->decap.ipip_entry->decap_fib_entry = NULL;
1097 fib_entry->decap.ipip_entry = NULL;
1098 mlxsw_sp_kvdl_free(mlxsw_sp, fib_entry->decap.tunnel_index);
1099}
1100
1cc38fb1
PM
1101static struct mlxsw_sp_fib_node *
1102mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
1103 size_t addr_len, unsigned char prefix_len);
4607f6d2
PM
1104static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
1105 struct mlxsw_sp_fib_entry *fib_entry);
1106
1107static void
1108mlxsw_sp_ipip_entry_demote_decap(struct mlxsw_sp *mlxsw_sp,
1109 struct mlxsw_sp_ipip_entry *ipip_entry)
1110{
1111 struct mlxsw_sp_fib_entry *fib_entry = ipip_entry->decap_fib_entry;
1112
1113 mlxsw_sp_fib_entry_decap_fini(mlxsw_sp, fib_entry);
1114 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
1115
1116 mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
1117}
1118
1cc38fb1
PM
1119static void
1120mlxsw_sp_ipip_entry_promote_decap(struct mlxsw_sp *mlxsw_sp,
1121 struct mlxsw_sp_ipip_entry *ipip_entry,
1122 struct mlxsw_sp_fib_entry *decap_fib_entry)
1123{
1124 if (mlxsw_sp_fib_entry_decap_init(mlxsw_sp, decap_fib_entry,
1125 ipip_entry))
1126 return;
1127 decap_fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP;
1128
1129 if (mlxsw_sp_fib_entry_update(mlxsw_sp, decap_fib_entry))
1130 mlxsw_sp_ipip_entry_demote_decap(mlxsw_sp, ipip_entry);
1131}
1132
1133/* Given an IPIP entry, find the corresponding decap route. */
1134static struct mlxsw_sp_fib_entry *
1135mlxsw_sp_ipip_entry_find_decap(struct mlxsw_sp *mlxsw_sp,
1136 struct mlxsw_sp_ipip_entry *ipip_entry)
1137{
1138 static struct mlxsw_sp_fib_node *fib_node;
1139 const struct mlxsw_sp_ipip_ops *ipip_ops;
1140 struct mlxsw_sp_fib_entry *fib_entry;
1141 unsigned char saddr_prefix_len;
1142 union mlxsw_sp_l3addr saddr;
1143 struct mlxsw_sp_fib *ul_fib;
1144 struct mlxsw_sp_vr *ul_vr;
1145 const void *saddrp;
1146 size_t saddr_len;
1147 u32 ul_tb_id;
1148 u32 saddr4;
1149
1150 ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt];
1151
1152 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ipip_entry->ol_dev);
1153 ul_vr = mlxsw_sp_vr_find(mlxsw_sp, ul_tb_id);
1154 if (!ul_vr)
1155 return NULL;
1156
1157 ul_fib = mlxsw_sp_vr_fib(ul_vr, ipip_ops->ul_proto);
1158 saddr = mlxsw_sp_ipip_netdev_saddr(ipip_ops->ul_proto,
1159 ipip_entry->ol_dev);
1160
1161 switch (ipip_ops->ul_proto) {
1162 case MLXSW_SP_L3_PROTO_IPV4:
1163 saddr4 = be32_to_cpu(saddr.addr4);
1164 saddrp = &saddr4;
1165 saddr_len = 4;
1166 saddr_prefix_len = 32;
1167 break;
1168 case MLXSW_SP_L3_PROTO_IPV6:
1169 WARN_ON(1);
1170 return NULL;
1171 }
1172
1173 fib_node = mlxsw_sp_fib_node_lookup(ul_fib, saddrp, saddr_len,
1174 saddr_prefix_len);
1175 if (!fib_node || list_empty(&fib_node->entry_list))
1176 return NULL;
1177
1178 fib_entry = list_first_entry(&fib_node->entry_list,
1179 struct mlxsw_sp_fib_entry, list);
1180 if (fib_entry->type != MLXSW_SP_FIB_ENTRY_TYPE_TRAP)
1181 return NULL;
1182
1183 return fib_entry;
1184}
1185
1012b9ac
PM
1186static struct mlxsw_sp_ipip_entry *
1187mlxsw_sp_ipip_entry_get(struct mlxsw_sp *mlxsw_sp,
1188 enum mlxsw_sp_ipip_type ipipt,
1189 struct net_device *ol_dev)
1190{
1191 u32 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ol_dev);
1192 struct mlxsw_sp_router *router = mlxsw_sp->router;
1cc38fb1 1193 struct mlxsw_sp_fib_entry *decap_fib_entry;
1012b9ac
PM
1194 struct mlxsw_sp_ipip_entry *ipip_entry;
1195 enum mlxsw_sp_l3proto ul_proto;
1196 union mlxsw_sp_l3addr saddr;
1197
1198 list_for_each_entry(ipip_entry, &mlxsw_sp->router->ipip_list,
1199 ipip_list_node) {
1200 if (ipip_entry->ol_dev == ol_dev)
1201 goto inc_ref_count;
1202
1203 /* The configuration where several tunnels have the same local
1204 * address in the same underlay table needs special treatment in
1205 * the HW. That is currently not implemented in the driver.
1206 */
1207 ul_proto = router->ipip_ops_arr[ipip_entry->ipipt]->ul_proto;
1208 saddr = mlxsw_sp_ipip_netdev_saddr(ul_proto, ol_dev);
1209 if (mlxsw_sp_ipip_entry_saddr_matches(mlxsw_sp, ul_proto, saddr,
1210 ul_tb_id, ipip_entry))
1211 return ERR_PTR(-EEXIST);
1212 }
1213
1214 ipip_entry = mlxsw_sp_ipip_entry_alloc(mlxsw_sp, ipipt, ol_dev);
1215 if (IS_ERR(ipip_entry))
1216 return ipip_entry;
1217
1cc38fb1
PM
1218 decap_fib_entry = mlxsw_sp_ipip_entry_find_decap(mlxsw_sp, ipip_entry);
1219 if (decap_fib_entry)
1220 mlxsw_sp_ipip_entry_promote_decap(mlxsw_sp, ipip_entry,
1221 decap_fib_entry);
1222
1012b9ac
PM
1223 list_add_tail(&ipip_entry->ipip_list_node,
1224 &mlxsw_sp->router->ipip_list);
1225
1226inc_ref_count:
1227 ++ipip_entry->ref_count;
1228 return ipip_entry;
1229}
1230
1231static void
1232mlxsw_sp_ipip_entry_put(struct mlxsw_sp *mlxsw_sp,
1233 struct mlxsw_sp_ipip_entry *ipip_entry)
1234{
1235 if (--ipip_entry->ref_count == 0) {
1236 list_del(&ipip_entry->ipip_list_node);
4607f6d2
PM
1237 if (ipip_entry->decap_fib_entry)
1238 mlxsw_sp_ipip_entry_demote_decap(mlxsw_sp, ipip_entry);
1012b9ac
PM
1239 mlxsw_sp_ipip_entry_destroy(ipip_entry);
1240 }
1241}
1242
4607f6d2
PM
1243static bool
1244mlxsw_sp_ipip_entry_matches_decap(struct mlxsw_sp *mlxsw_sp,
1245 const struct net_device *ul_dev,
1246 enum mlxsw_sp_l3proto ul_proto,
1247 union mlxsw_sp_l3addr ul_dip,
1248 struct mlxsw_sp_ipip_entry *ipip_entry)
1249{
1250 u32 ul_tb_id = l3mdev_fib_table(ul_dev) ? : RT_TABLE_MAIN;
1251 enum mlxsw_sp_ipip_type ipipt = ipip_entry->ipipt;
1252 struct net_device *ipip_ul_dev;
1253
1254 if (mlxsw_sp->router->ipip_ops_arr[ipipt]->ul_proto != ul_proto)
1255 return false;
1256
1257 ipip_ul_dev = __mlxsw_sp_ipip_netdev_ul_dev_get(ipip_entry->ol_dev);
1258 return mlxsw_sp_ipip_entry_saddr_matches(mlxsw_sp, ul_proto, ul_dip,
1259 ul_tb_id, ipip_entry) &&
1260 (!ipip_ul_dev || ipip_ul_dev == ul_dev);
1261}
1262
1263/* Given decap parameters, find the corresponding IPIP entry. */
1264static struct mlxsw_sp_ipip_entry *
1265mlxsw_sp_ipip_entry_find_by_decap(struct mlxsw_sp *mlxsw_sp,
1266 const struct net_device *ul_dev,
1267 enum mlxsw_sp_l3proto ul_proto,
1268 union mlxsw_sp_l3addr ul_dip)
1269{
1270 struct mlxsw_sp_ipip_entry *ipip_entry;
1271
1272 list_for_each_entry(ipip_entry, &mlxsw_sp->router->ipip_list,
1273 ipip_list_node)
1274 if (mlxsw_sp_ipip_entry_matches_decap(mlxsw_sp, ul_dev,
1275 ul_proto, ul_dip,
1276 ipip_entry))
1277 return ipip_entry;
1278
1279 return NULL;
1280}
1281
6cf3c971 1282struct mlxsw_sp_neigh_key {
33b1341c 1283 struct neighbour *n;
6cf3c971
JP
1284};
1285
1286struct mlxsw_sp_neigh_entry {
9665b745 1287 struct list_head rif_list_node;
6cf3c971
JP
1288 struct rhash_head ht_node;
1289 struct mlxsw_sp_neigh_key key;
1290 u16 rif;
5c8802f1 1291 bool connected;
a6bf9e93 1292 unsigned char ha[ETH_ALEN];
a7ff87ac
JP
1293 struct list_head nexthop_list; /* list of nexthops using
1294 * this neigh entry
1295 */
b2157149 1296 struct list_head nexthop_neighs_list_node;
7cfcbc75
AS
1297 unsigned int counter_index;
1298 bool counter_valid;
6cf3c971
JP
1299};
1300
1301static const struct rhashtable_params mlxsw_sp_neigh_ht_params = {
1302 .key_offset = offsetof(struct mlxsw_sp_neigh_entry, key),
1303 .head_offset = offsetof(struct mlxsw_sp_neigh_entry, ht_node),
1304 .key_len = sizeof(struct mlxsw_sp_neigh_key),
1305};
1306
f17cc84d
AS
1307struct mlxsw_sp_neigh_entry *
1308mlxsw_sp_rif_neigh_next(struct mlxsw_sp_rif *rif,
1309 struct mlxsw_sp_neigh_entry *neigh_entry)
1310{
1311 if (!neigh_entry) {
1312 if (list_empty(&rif->neigh_list))
1313 return NULL;
1314 else
1315 return list_first_entry(&rif->neigh_list,
1316 typeof(*neigh_entry),
1317 rif_list_node);
1318 }
1319 if (neigh_entry->rif_list_node.next == &rif->neigh_list)
1320 return NULL;
1321 return list_next_entry(neigh_entry, rif_list_node);
1322}
1323
1324int mlxsw_sp_neigh_entry_type(struct mlxsw_sp_neigh_entry *neigh_entry)
1325{
1326 return neigh_entry->key.n->tbl->family;
1327}
1328
1329unsigned char *
1330mlxsw_sp_neigh_entry_ha(struct mlxsw_sp_neigh_entry *neigh_entry)
1331{
1332 return neigh_entry->ha;
1333}
1334
1335u32 mlxsw_sp_neigh4_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry)
1336{
1337 struct neighbour *n;
1338
1339 n = neigh_entry->key.n;
1340 return ntohl(*((__be32 *) n->primary_key));
1341}
1342
0250768c
AS
1343struct in6_addr *
1344mlxsw_sp_neigh6_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry)
1345{
1346 struct neighbour *n;
1347
1348 n = neigh_entry->key.n;
1349 return (struct in6_addr *) &n->primary_key;
1350}
1351
7cfcbc75
AS
1352int mlxsw_sp_neigh_counter_get(struct mlxsw_sp *mlxsw_sp,
1353 struct mlxsw_sp_neigh_entry *neigh_entry,
1354 u64 *p_counter)
1355{
1356 if (!neigh_entry->counter_valid)
1357 return -EINVAL;
1358
1359 return mlxsw_sp_flow_counter_get(mlxsw_sp, neigh_entry->counter_index,
1360 p_counter, NULL);
1361}
1362
6cf3c971 1363static struct mlxsw_sp_neigh_entry *
5c8802f1
IS
1364mlxsw_sp_neigh_entry_alloc(struct mlxsw_sp *mlxsw_sp, struct neighbour *n,
1365 u16 rif)
6cf3c971
JP
1366{
1367 struct mlxsw_sp_neigh_entry *neigh_entry;
1368
5c8802f1 1369 neigh_entry = kzalloc(sizeof(*neigh_entry), GFP_KERNEL);
6cf3c971
JP
1370 if (!neigh_entry)
1371 return NULL;
5c8802f1 1372
33b1341c 1373 neigh_entry->key.n = n;
6cf3c971 1374 neigh_entry->rif = rif;
a7ff87ac 1375 INIT_LIST_HEAD(&neigh_entry->nexthop_list);
5c8802f1 1376
6cf3c971
JP
1377 return neigh_entry;
1378}
1379
5c8802f1 1380static void mlxsw_sp_neigh_entry_free(struct mlxsw_sp_neigh_entry *neigh_entry)
6cf3c971
JP
1381{
1382 kfree(neigh_entry);
1383}
1384
5c8802f1
IS
1385static int
1386mlxsw_sp_neigh_entry_insert(struct mlxsw_sp *mlxsw_sp,
1387 struct mlxsw_sp_neigh_entry *neigh_entry)
6cf3c971 1388{
9011b677 1389 return rhashtable_insert_fast(&mlxsw_sp->router->neigh_ht,
5c8802f1
IS
1390 &neigh_entry->ht_node,
1391 mlxsw_sp_neigh_ht_params);
1392}
6cf3c971 1393
5c8802f1
IS
1394static void
1395mlxsw_sp_neigh_entry_remove(struct mlxsw_sp *mlxsw_sp,
1396 struct mlxsw_sp_neigh_entry *neigh_entry)
1397{
9011b677 1398 rhashtable_remove_fast(&mlxsw_sp->router->neigh_ht,
5c8802f1
IS
1399 &neigh_entry->ht_node,
1400 mlxsw_sp_neigh_ht_params);
6cf3c971
JP
1401}
1402
7cfcbc75 1403static bool
1ed5574c
AS
1404mlxsw_sp_neigh_counter_should_alloc(struct mlxsw_sp *mlxsw_sp,
1405 struct mlxsw_sp_neigh_entry *neigh_entry)
7cfcbc75
AS
1406{
1407 struct devlink *devlink;
1ed5574c
AS
1408 const char *table_name;
1409
1410 switch (mlxsw_sp_neigh_entry_type(neigh_entry)) {
1411 case AF_INET:
1412 table_name = MLXSW_SP_DPIPE_TABLE_NAME_HOST4;
1413 break;
1414 case AF_INET6:
1415 table_name = MLXSW_SP_DPIPE_TABLE_NAME_HOST6;
1416 break;
1417 default:
1418 WARN_ON(1);
1419 return false;
1420 }
7cfcbc75
AS
1421
1422 devlink = priv_to_devlink(mlxsw_sp->core);
1ed5574c 1423 return devlink_dpipe_table_counter_enabled(devlink, table_name);
7cfcbc75
AS
1424}
1425
1426static void
1427mlxsw_sp_neigh_counter_alloc(struct mlxsw_sp *mlxsw_sp,
1428 struct mlxsw_sp_neigh_entry *neigh_entry)
1429{
1ed5574c 1430 if (!mlxsw_sp_neigh_counter_should_alloc(mlxsw_sp, neigh_entry))
7cfcbc75
AS
1431 return;
1432
1433 if (mlxsw_sp_flow_counter_alloc(mlxsw_sp, &neigh_entry->counter_index))
1434 return;
1435
1436 neigh_entry->counter_valid = true;
1437}
1438
1439static void
1440mlxsw_sp_neigh_counter_free(struct mlxsw_sp *mlxsw_sp,
1441 struct mlxsw_sp_neigh_entry *neigh_entry)
1442{
1443 if (!neigh_entry->counter_valid)
1444 return;
1445 mlxsw_sp_flow_counter_free(mlxsw_sp,
1446 neigh_entry->counter_index);
1447 neigh_entry->counter_valid = false;
1448}
1449
5c8802f1
IS
1450static struct mlxsw_sp_neigh_entry *
1451mlxsw_sp_neigh_entry_create(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
6cf3c971 1452{
6cf3c971 1453 struct mlxsw_sp_neigh_entry *neigh_entry;
bf95233e 1454 struct mlxsw_sp_rif *rif;
6cf3c971
JP
1455 int err;
1456
bf95233e
AS
1457 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, n->dev);
1458 if (!rif)
5c8802f1 1459 return ERR_PTR(-EINVAL);
6cf3c971 1460
bf95233e 1461 neigh_entry = mlxsw_sp_neigh_entry_alloc(mlxsw_sp, n, rif->rif_index);
6cf3c971 1462 if (!neigh_entry)
5c8802f1
IS
1463 return ERR_PTR(-ENOMEM);
1464
6cf3c971
JP
1465 err = mlxsw_sp_neigh_entry_insert(mlxsw_sp, neigh_entry);
1466 if (err)
1467 goto err_neigh_entry_insert;
5c8802f1 1468
7cfcbc75 1469 mlxsw_sp_neigh_counter_alloc(mlxsw_sp, neigh_entry);
bf95233e 1470 list_add(&neigh_entry->rif_list_node, &rif->neigh_list);
9665b745 1471
5c8802f1 1472 return neigh_entry;
6cf3c971
JP
1473
1474err_neigh_entry_insert:
5c8802f1
IS
1475 mlxsw_sp_neigh_entry_free(neigh_entry);
1476 return ERR_PTR(err);
6cf3c971
JP
1477}
1478
5c8802f1
IS
1479static void
1480mlxsw_sp_neigh_entry_destroy(struct mlxsw_sp *mlxsw_sp,
1481 struct mlxsw_sp_neigh_entry *neigh_entry)
6cf3c971 1482{
9665b745 1483 list_del(&neigh_entry->rif_list_node);
7cfcbc75 1484 mlxsw_sp_neigh_counter_free(mlxsw_sp, neigh_entry);
5c8802f1
IS
1485 mlxsw_sp_neigh_entry_remove(mlxsw_sp, neigh_entry);
1486 mlxsw_sp_neigh_entry_free(neigh_entry);
1487}
6cf3c971 1488
5c8802f1
IS
1489static struct mlxsw_sp_neigh_entry *
1490mlxsw_sp_neigh_entry_lookup(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
1491{
1492 struct mlxsw_sp_neigh_key key;
6cf3c971 1493
5c8802f1 1494 key.n = n;
9011b677 1495 return rhashtable_lookup_fast(&mlxsw_sp->router->neigh_ht,
5c8802f1 1496 &key, mlxsw_sp_neigh_ht_params);
6cf3c971
JP
1497}
1498
c723c735
YG
1499static void
1500mlxsw_sp_router_neighs_update_interval_init(struct mlxsw_sp *mlxsw_sp)
1501{
a6c9b5d1 1502 unsigned long interval;
c723c735 1503
b5f3e0d4 1504#if IS_ENABLED(CONFIG_IPV6)
a6c9b5d1
AS
1505 interval = min_t(unsigned long,
1506 NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME),
1507 NEIGH_VAR(&nd_tbl.parms, DELAY_PROBE_TIME));
b5f3e0d4
IS
1508#else
1509 interval = NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME);
1510#endif
9011b677 1511 mlxsw_sp->router->neighs_update.interval = jiffies_to_msecs(interval);
c723c735
YG
1512}
1513
1514static void mlxsw_sp_router_neigh_ent_ipv4_process(struct mlxsw_sp *mlxsw_sp,
1515 char *rauhtd_pl,
1516 int ent_index)
1517{
1518 struct net_device *dev;
1519 struct neighbour *n;
1520 __be32 dipn;
1521 u32 dip;
1522 u16 rif;
1523
1524 mlxsw_reg_rauhtd_ent_ipv4_unpack(rauhtd_pl, ent_index, &rif, &dip);
1525
5f9efffb 1526 if (!mlxsw_sp->router->rifs[rif]) {
c723c735
YG
1527 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
1528 return;
1529 }
1530
1531 dipn = htonl(dip);
5f9efffb 1532 dev = mlxsw_sp->router->rifs[rif]->dev;
c723c735
YG
1533 n = neigh_lookup(&arp_tbl, &dipn, dev);
1534 if (!n) {
1535 netdev_err(dev, "Failed to find matching neighbour for IP=%pI4h\n",
1536 &dip);
1537 return;
1538 }
1539
1540 netdev_dbg(dev, "Updating neighbour with IP=%pI4h\n", &dip);
1541 neigh_event_send(n, NULL);
1542 neigh_release(n);
1543}
1544
df9a21f1 1545#if IS_ENABLED(CONFIG_IPV6)
60f040ca
AS
1546static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1547 char *rauhtd_pl,
1548 int rec_index)
1549{
1550 struct net_device *dev;
1551 struct neighbour *n;
1552 struct in6_addr dip;
1553 u16 rif;
1554
1555 mlxsw_reg_rauhtd_ent_ipv6_unpack(rauhtd_pl, rec_index, &rif,
1556 (char *) &dip);
1557
1558 if (!mlxsw_sp->router->rifs[rif]) {
1559 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
1560 return;
1561 }
1562
1563 dev = mlxsw_sp->router->rifs[rif]->dev;
1564 n = neigh_lookup(&nd_tbl, &dip, dev);
1565 if (!n) {
1566 netdev_err(dev, "Failed to find matching neighbour for IP=%pI6c\n",
1567 &dip);
1568 return;
1569 }
1570
1571 netdev_dbg(dev, "Updating neighbour with IP=%pI6c\n", &dip);
1572 neigh_event_send(n, NULL);
1573 neigh_release(n);
1574}
b5f3e0d4
IS
1575#else
1576static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1577 char *rauhtd_pl,
1578 int rec_index)
1579{
1580}
1581#endif
60f040ca 1582
c723c735
YG
1583static void mlxsw_sp_router_neigh_rec_ipv4_process(struct mlxsw_sp *mlxsw_sp,
1584 char *rauhtd_pl,
1585 int rec_index)
1586{
1587 u8 num_entries;
1588 int i;
1589
1590 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
1591 rec_index);
1592 /* Hardware starts counting at 0, so add 1. */
1593 num_entries++;
1594
1595 /* Each record consists of several neighbour entries. */
1596 for (i = 0; i < num_entries; i++) {
1597 int ent_index;
1598
1599 ent_index = rec_index * MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC + i;
1600 mlxsw_sp_router_neigh_ent_ipv4_process(mlxsw_sp, rauhtd_pl,
1601 ent_index);
1602 }
1603
1604}
1605
60f040ca
AS
1606static void mlxsw_sp_router_neigh_rec_ipv6_process(struct mlxsw_sp *mlxsw_sp,
1607 char *rauhtd_pl,
1608 int rec_index)
1609{
1610 /* One record contains one entry. */
1611 mlxsw_sp_router_neigh_ent_ipv6_process(mlxsw_sp, rauhtd_pl,
1612 rec_index);
1613}
1614
c723c735
YG
1615static void mlxsw_sp_router_neigh_rec_process(struct mlxsw_sp *mlxsw_sp,
1616 char *rauhtd_pl, int rec_index)
1617{
1618 switch (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, rec_index)) {
1619 case MLXSW_REG_RAUHTD_TYPE_IPV4:
1620 mlxsw_sp_router_neigh_rec_ipv4_process(mlxsw_sp, rauhtd_pl,
1621 rec_index);
1622 break;
1623 case MLXSW_REG_RAUHTD_TYPE_IPV6:
60f040ca
AS
1624 mlxsw_sp_router_neigh_rec_ipv6_process(mlxsw_sp, rauhtd_pl,
1625 rec_index);
c723c735
YG
1626 break;
1627 }
1628}
1629
42cdb338
AS
1630static bool mlxsw_sp_router_rauhtd_is_full(char *rauhtd_pl)
1631{
1632 u8 num_rec, last_rec_index, num_entries;
1633
1634 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
1635 last_rec_index = num_rec - 1;
1636
1637 if (num_rec < MLXSW_REG_RAUHTD_REC_MAX_NUM)
1638 return false;
1639 if (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, last_rec_index) ==
1640 MLXSW_REG_RAUHTD_TYPE_IPV6)
1641 return true;
1642
1643 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
1644 last_rec_index);
1645 if (++num_entries == MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC)
1646 return true;
1647 return false;
1648}
1649
60f040ca
AS
1650static int
1651__mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp,
1652 char *rauhtd_pl,
1653 enum mlxsw_reg_rauhtd_type type)
c723c735 1654{
60f040ca
AS
1655 int i, num_rec;
1656 int err;
c723c735
YG
1657
1658 /* Make sure the neighbour's netdev isn't removed in the
1659 * process.
1660 */
1661 rtnl_lock();
1662 do {
60f040ca 1663 mlxsw_reg_rauhtd_pack(rauhtd_pl, type);
c723c735
YG
1664 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(rauhtd),
1665 rauhtd_pl);
1666 if (err) {
1667 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to dump neighbour talbe\n");
1668 break;
1669 }
1670 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
1671 for (i = 0; i < num_rec; i++)
1672 mlxsw_sp_router_neigh_rec_process(mlxsw_sp, rauhtd_pl,
1673 i);
42cdb338 1674 } while (mlxsw_sp_router_rauhtd_is_full(rauhtd_pl));
c723c735
YG
1675 rtnl_unlock();
1676
60f040ca
AS
1677 return err;
1678}
1679
1680static int mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp)
1681{
1682 enum mlxsw_reg_rauhtd_type type;
1683 char *rauhtd_pl;
1684 int err;
1685
1686 rauhtd_pl = kmalloc(MLXSW_REG_RAUHTD_LEN, GFP_KERNEL);
1687 if (!rauhtd_pl)
1688 return -ENOMEM;
1689
1690 type = MLXSW_REG_RAUHTD_TYPE_IPV4;
1691 err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);
1692 if (err)
1693 goto out;
1694
1695 type = MLXSW_REG_RAUHTD_TYPE_IPV6;
1696 err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type);
1697out:
c723c735 1698 kfree(rauhtd_pl);
b2157149
YG
1699 return err;
1700}
1701
1702static void mlxsw_sp_router_neighs_update_nh(struct mlxsw_sp *mlxsw_sp)
1703{
1704 struct mlxsw_sp_neigh_entry *neigh_entry;
1705
1706 /* Take RTNL mutex here to prevent lists from changes */
1707 rtnl_lock();
9011b677 1708 list_for_each_entry(neigh_entry, &mlxsw_sp->router->nexthop_neighs_list,
8a0b7275 1709 nexthop_neighs_list_node)
b2157149
YG
1710 /* If this neigh have nexthops, make the kernel think this neigh
1711 * is active regardless of the traffic.
1712 */
8a0b7275 1713 neigh_event_send(neigh_entry->key.n, NULL);
b2157149
YG
1714 rtnl_unlock();
1715}
1716
1717static void
1718mlxsw_sp_router_neighs_update_work_schedule(struct mlxsw_sp *mlxsw_sp)
1719{
9011b677 1720 unsigned long interval = mlxsw_sp->router->neighs_update.interval;
b2157149 1721
9011b677 1722 mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw,
b2157149
YG
1723 msecs_to_jiffies(interval));
1724}
1725
1726static void mlxsw_sp_router_neighs_update_work(struct work_struct *work)
1727{
9011b677 1728 struct mlxsw_sp_router *router;
b2157149
YG
1729 int err;
1730
9011b677
IS
1731 router = container_of(work, struct mlxsw_sp_router,
1732 neighs_update.dw.work);
1733 err = mlxsw_sp_router_neighs_update_rauhtd(router->mlxsw_sp);
b2157149 1734 if (err)
9011b677 1735 dev_err(router->mlxsw_sp->bus_info->dev, "Could not update kernel for neigh activity");
b2157149 1736
9011b677 1737 mlxsw_sp_router_neighs_update_nh(router->mlxsw_sp);
b2157149 1738
9011b677 1739 mlxsw_sp_router_neighs_update_work_schedule(router->mlxsw_sp);
c723c735
YG
1740}
1741
0b2361d9
YG
1742static void mlxsw_sp_router_probe_unresolved_nexthops(struct work_struct *work)
1743{
1744 struct mlxsw_sp_neigh_entry *neigh_entry;
9011b677 1745 struct mlxsw_sp_router *router;
0b2361d9 1746
9011b677
IS
1747 router = container_of(work, struct mlxsw_sp_router,
1748 nexthop_probe_dw.work);
0b2361d9
YG
1749 /* Iterate over nexthop neighbours, find those who are unresolved and
1750 * send arp on them. This solves the chicken-egg problem when
1751 * the nexthop wouldn't get offloaded until the neighbor is resolved
1752 * but it wouldn't get resolved ever in case traffic is flowing in HW
1753 * using different nexthop.
1754 *
1755 * Take RTNL mutex here to prevent lists from changes.
1756 */
1757 rtnl_lock();
9011b677 1758 list_for_each_entry(neigh_entry, &router->nexthop_neighs_list,
8a0b7275 1759 nexthop_neighs_list_node)
01b1aa35 1760 if (!neigh_entry->connected)
33b1341c 1761 neigh_event_send(neigh_entry->key.n, NULL);
0b2361d9
YG
1762 rtnl_unlock();
1763
9011b677 1764 mlxsw_core_schedule_dw(&router->nexthop_probe_dw,
0b2361d9
YG
1765 MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL);
1766}
1767
a7ff87ac
JP
1768static void
1769mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
1770 struct mlxsw_sp_neigh_entry *neigh_entry,
1771 bool removing);
1772
5c8802f1
IS
1773static enum mlxsw_reg_rauht_op mlxsw_sp_rauht_op(bool adding)
1774{
1775 return adding ? MLXSW_REG_RAUHT_OP_WRITE_ADD :
1776 MLXSW_REG_RAUHT_OP_WRITE_DELETE;
1777}
1778
1779static void
1780mlxsw_sp_router_neigh_entry_op4(struct mlxsw_sp *mlxsw_sp,
1781 struct mlxsw_sp_neigh_entry *neigh_entry,
1782 enum mlxsw_reg_rauht_op op)
a6bf9e93 1783{
33b1341c 1784 struct neighbour *n = neigh_entry->key.n;
5c8802f1 1785 u32 dip = ntohl(*((__be32 *) n->primary_key));
a6bf9e93 1786 char rauht_pl[MLXSW_REG_RAUHT_LEN];
5c8802f1
IS
1787
1788 mlxsw_reg_rauht_pack4(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
1789 dip);
7cfcbc75
AS
1790 if (neigh_entry->counter_valid)
1791 mlxsw_reg_rauht_pack_counter(rauht_pl,
1792 neigh_entry->counter_index);
5c8802f1
IS
1793 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
1794}
1795
d5eb89cf
AS
1796static void
1797mlxsw_sp_router_neigh_entry_op6(struct mlxsw_sp *mlxsw_sp,
1798 struct mlxsw_sp_neigh_entry *neigh_entry,
1799 enum mlxsw_reg_rauht_op op)
1800{
1801 struct neighbour *n = neigh_entry->key.n;
1802 char rauht_pl[MLXSW_REG_RAUHT_LEN];
1803 const char *dip = n->primary_key;
1804
1805 mlxsw_reg_rauht_pack6(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
1806 dip);
7cfcbc75
AS
1807 if (neigh_entry->counter_valid)
1808 mlxsw_reg_rauht_pack_counter(rauht_pl,
1809 neigh_entry->counter_index);
d5eb89cf
AS
1810 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
1811}
1812
1d1056d8 1813bool mlxsw_sp_neigh_ipv6_ignore(struct mlxsw_sp_neigh_entry *neigh_entry)
d5eb89cf 1814{
1d1056d8
AS
1815 struct neighbour *n = neigh_entry->key.n;
1816
d5eb89cf
AS
1817 /* Packets with a link-local destination address are trapped
1818 * after LPM lookup and never reach the neighbour table, so
1819 * there is no need to program such neighbours to the device.
1820 */
1821 if (ipv6_addr_type((struct in6_addr *) &n->primary_key) &
1822 IPV6_ADDR_LINKLOCAL)
1823 return true;
1824 return false;
1825}
1826
5c8802f1
IS
1827static void
1828mlxsw_sp_neigh_entry_update(struct mlxsw_sp *mlxsw_sp,
1829 struct mlxsw_sp_neigh_entry *neigh_entry,
1830 bool adding)
1831{
1832 if (!adding && !neigh_entry->connected)
1833 return;
1834 neigh_entry->connected = adding;
b5f3e0d4 1835 if (neigh_entry->key.n->tbl->family == AF_INET) {
5c8802f1
IS
1836 mlxsw_sp_router_neigh_entry_op4(mlxsw_sp, neigh_entry,
1837 mlxsw_sp_rauht_op(adding));
b5f3e0d4 1838 } else if (neigh_entry->key.n->tbl->family == AF_INET6) {
1d1056d8 1839 if (mlxsw_sp_neigh_ipv6_ignore(neigh_entry))
d5eb89cf
AS
1840 return;
1841 mlxsw_sp_router_neigh_entry_op6(mlxsw_sp, neigh_entry,
1842 mlxsw_sp_rauht_op(adding));
1843 } else {
5c8802f1 1844 WARN_ON_ONCE(1);
d5eb89cf 1845 }
5c8802f1
IS
1846}
1847
a481d713
AS
1848void
1849mlxsw_sp_neigh_entry_counter_update(struct mlxsw_sp *mlxsw_sp,
1850 struct mlxsw_sp_neigh_entry *neigh_entry,
1851 bool adding)
1852{
1853 if (adding)
1854 mlxsw_sp_neigh_counter_alloc(mlxsw_sp, neigh_entry);
1855 else
1856 mlxsw_sp_neigh_counter_free(mlxsw_sp, neigh_entry);
1857 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, true);
1858}
1859
5c8802f1
IS
1860struct mlxsw_sp_neigh_event_work {
1861 struct work_struct work;
1862 struct mlxsw_sp *mlxsw_sp;
1863 struct neighbour *n;
1864};
1865
1866static void mlxsw_sp_router_neigh_event_work(struct work_struct *work)
1867{
1868 struct mlxsw_sp_neigh_event_work *neigh_work =
1869 container_of(work, struct mlxsw_sp_neigh_event_work, work);
1870 struct mlxsw_sp *mlxsw_sp = neigh_work->mlxsw_sp;
1871 struct mlxsw_sp_neigh_entry *neigh_entry;
1872 struct neighbour *n = neigh_work->n;
1873 unsigned char ha[ETH_ALEN];
a6bf9e93 1874 bool entry_connected;
93a87e5e 1875 u8 nud_state, dead;
a6bf9e93 1876
5c8802f1
IS
1877 /* If these parameters are changed after we release the lock,
1878 * then we are guaranteed to receive another event letting us
1879 * know about it.
1880 */
a6bf9e93 1881 read_lock_bh(&n->lock);
5c8802f1 1882 memcpy(ha, n->ha, ETH_ALEN);
a6bf9e93 1883 nud_state = n->nud_state;
93a87e5e 1884 dead = n->dead;
a6bf9e93
YG
1885 read_unlock_bh(&n->lock);
1886
5c8802f1 1887 rtnl_lock();
93a87e5e 1888 entry_connected = nud_state & NUD_VALID && !dead;
5c8802f1
IS
1889 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
1890 if (!entry_connected && !neigh_entry)
1891 goto out;
1892 if (!neigh_entry) {
1893 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
1894 if (IS_ERR(neigh_entry))
1895 goto out;
a6bf9e93
YG
1896 }
1897
5c8802f1
IS
1898 memcpy(neigh_entry->ha, ha, ETH_ALEN);
1899 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, entry_connected);
1900 mlxsw_sp_nexthop_neigh_update(mlxsw_sp, neigh_entry, !entry_connected);
1901
1902 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
1903 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
1904
1905out:
1906 rtnl_unlock();
a6bf9e93 1907 neigh_release(n);
5c8802f1 1908 kfree(neigh_work);
a6bf9e93
YG
1909}
1910
e7322638
JP
1911int mlxsw_sp_router_netevent_event(struct notifier_block *unused,
1912 unsigned long event, void *ptr)
c723c735 1913{
5c8802f1 1914 struct mlxsw_sp_neigh_event_work *neigh_work;
c723c735
YG
1915 struct mlxsw_sp_port *mlxsw_sp_port;
1916 struct mlxsw_sp *mlxsw_sp;
1917 unsigned long interval;
1918 struct neigh_parms *p;
a6bf9e93 1919 struct neighbour *n;
c723c735
YG
1920
1921 switch (event) {
1922 case NETEVENT_DELAY_PROBE_TIME_UPDATE:
1923 p = ptr;
1924
1925 /* We don't care about changes in the default table. */
b5f3e0d4
IS
1926 if (!p->dev || (p->tbl->family != AF_INET &&
1927 p->tbl->family != AF_INET6))
c723c735
YG
1928 return NOTIFY_DONE;
1929
1930 /* We are in atomic context and can't take RTNL mutex,
1931 * so use RCU variant to walk the device chain.
1932 */
1933 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(p->dev);
1934 if (!mlxsw_sp_port)
1935 return NOTIFY_DONE;
1936
1937 mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1938 interval = jiffies_to_msecs(NEIGH_VAR(p, DELAY_PROBE_TIME));
9011b677 1939 mlxsw_sp->router->neighs_update.interval = interval;
c723c735
YG
1940
1941 mlxsw_sp_port_dev_put(mlxsw_sp_port);
1942 break;
a6bf9e93
YG
1943 case NETEVENT_NEIGH_UPDATE:
1944 n = ptr;
a6bf9e93 1945
b5f3e0d4 1946 if (n->tbl->family != AF_INET && n->tbl->family != AF_INET6)
a6bf9e93
YG
1947 return NOTIFY_DONE;
1948
5c8802f1 1949 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(n->dev);
a6bf9e93
YG
1950 if (!mlxsw_sp_port)
1951 return NOTIFY_DONE;
1952
5c8802f1
IS
1953 neigh_work = kzalloc(sizeof(*neigh_work), GFP_ATOMIC);
1954 if (!neigh_work) {
a6bf9e93 1955 mlxsw_sp_port_dev_put(mlxsw_sp_port);
5c8802f1 1956 return NOTIFY_BAD;
a6bf9e93 1957 }
5c8802f1
IS
1958
1959 INIT_WORK(&neigh_work->work, mlxsw_sp_router_neigh_event_work);
1960 neigh_work->mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1961 neigh_work->n = n;
a6bf9e93
YG
1962
1963 /* Take a reference to ensure the neighbour won't be
1964 * destructed until we drop the reference in delayed
1965 * work.
1966 */
1967 neigh_clone(n);
5c8802f1
IS
1968 mlxsw_core_schedule_work(&neigh_work->work);
1969 mlxsw_sp_port_dev_put(mlxsw_sp_port);
a6bf9e93 1970 break;
c723c735
YG
1971 }
1972
1973 return NOTIFY_DONE;
1974}
1975
6cf3c971
JP
1976static int mlxsw_sp_neigh_init(struct mlxsw_sp *mlxsw_sp)
1977{
c723c735
YG
1978 int err;
1979
9011b677 1980 err = rhashtable_init(&mlxsw_sp->router->neigh_ht,
c723c735
YG
1981 &mlxsw_sp_neigh_ht_params);
1982 if (err)
1983 return err;
1984
1985 /* Initialize the polling interval according to the default
1986 * table.
1987 */
1988 mlxsw_sp_router_neighs_update_interval_init(mlxsw_sp);
1989
0b2361d9 1990 /* Create the delayed works for the activity_update */
9011b677 1991 INIT_DELAYED_WORK(&mlxsw_sp->router->neighs_update.dw,
c723c735 1992 mlxsw_sp_router_neighs_update_work);
9011b677 1993 INIT_DELAYED_WORK(&mlxsw_sp->router->nexthop_probe_dw,
0b2361d9 1994 mlxsw_sp_router_probe_unresolved_nexthops);
9011b677
IS
1995 mlxsw_core_schedule_dw(&mlxsw_sp->router->neighs_update.dw, 0);
1996 mlxsw_core_schedule_dw(&mlxsw_sp->router->nexthop_probe_dw, 0);
c723c735 1997 return 0;
6cf3c971
JP
1998}
1999
2000static void mlxsw_sp_neigh_fini(struct mlxsw_sp *mlxsw_sp)
2001{
9011b677
IS
2002 cancel_delayed_work_sync(&mlxsw_sp->router->neighs_update.dw);
2003 cancel_delayed_work_sync(&mlxsw_sp->router->nexthop_probe_dw);
2004 rhashtable_destroy(&mlxsw_sp->router->neigh_ht);
6cf3c971
JP
2005}
2006
9665b745 2007static void mlxsw_sp_neigh_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
bf95233e 2008 struct mlxsw_sp_rif *rif)
9665b745
IS
2009{
2010 struct mlxsw_sp_neigh_entry *neigh_entry, *tmp;
2011
bf95233e 2012 list_for_each_entry_safe(neigh_entry, tmp, &rif->neigh_list,
4a3c67a6
IS
2013 rif_list_node) {
2014 mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, false);
9665b745 2015 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
4a3c67a6 2016 }
9665b745
IS
2017}
2018
35225e47
PM
2019enum mlxsw_sp_nexthop_type {
2020 MLXSW_SP_NEXTHOP_TYPE_ETH,
1012b9ac 2021 MLXSW_SP_NEXTHOP_TYPE_IPIP,
35225e47
PM
2022};
2023
c53b8e1b
IS
2024struct mlxsw_sp_nexthop_key {
2025 struct fib_nh *fib_nh;
2026};
2027
a7ff87ac
JP
2028struct mlxsw_sp_nexthop {
2029 struct list_head neigh_list_node; /* member of neigh entry list */
9665b745 2030 struct list_head rif_list_node;
a7ff87ac
JP
2031 struct mlxsw_sp_nexthop_group *nh_grp; /* pointer back to the group
2032 * this belongs to
2033 */
c53b8e1b
IS
2034 struct rhash_head ht_node;
2035 struct mlxsw_sp_nexthop_key key;
58adf2c4 2036 unsigned char gw_addr[sizeof(struct in6_addr)];
e6f3b379 2037 int ifindex;
bf95233e 2038 struct mlxsw_sp_rif *rif;
a7ff87ac
JP
2039 u8 should_offload:1, /* set indicates this neigh is connected and
2040 * should be put to KVD linear area of this group.
2041 */
2042 offloaded:1, /* set in case the neigh is actually put into
2043 * KVD linear area of this group.
2044 */
2045 update:1; /* set indicates that MAC of this neigh should be
2046 * updated in HW
2047 */
35225e47
PM
2048 enum mlxsw_sp_nexthop_type type;
2049 union {
2050 struct mlxsw_sp_neigh_entry *neigh_entry;
1012b9ac 2051 struct mlxsw_sp_ipip_entry *ipip_entry;
35225e47 2052 };
a7ff87ac
JP
2053};
2054
2055struct mlxsw_sp_nexthop_group {
ba31d366 2056 void *priv;
e9ad5e7d 2057 struct rhash_head ht_node;
a7ff87ac 2058 struct list_head fib_list; /* list of fib entries that use this group */
58adf2c4 2059 struct neigh_table *neigh_tbl;
b3e8d1eb
IS
2060 u8 adj_index_valid:1,
2061 gateway:1; /* routes using the group use a gateway */
a7ff87ac
JP
2062 u32 adj_index;
2063 u16 ecmp_size;
2064 u16 count;
2065 struct mlxsw_sp_nexthop nexthops[0];
bf95233e 2066#define nh_rif nexthops[0].rif
a7ff87ac
JP
2067};
2068
ba31d366
AS
2069static struct fib_info *
2070mlxsw_sp_nexthop4_group_fi(const struct mlxsw_sp_nexthop_group *nh_grp)
2071{
2072 return nh_grp->priv;
2073}
2074
2075struct mlxsw_sp_nexthop_group_cmp_arg {
e6f3b379
AS
2076 enum mlxsw_sp_l3proto proto;
2077 union {
2078 struct fib_info *fi;
2079 struct mlxsw_sp_fib6_entry *fib6_entry;
2080 };
ba31d366
AS
2081};
2082
e6f3b379
AS
2083static bool
2084mlxsw_sp_nexthop6_group_has_nexthop(const struct mlxsw_sp_nexthop_group *nh_grp,
2085 const struct in6_addr *gw, int ifindex)
2086{
2087 int i;
2088
2089 for (i = 0; i < nh_grp->count; i++) {
2090 const struct mlxsw_sp_nexthop *nh;
2091
2092 nh = &nh_grp->nexthops[i];
2093 if (nh->ifindex == ifindex &&
2094 ipv6_addr_equal(gw, (struct in6_addr *) nh->gw_addr))
2095 return true;
2096 }
2097
2098 return false;
2099}
2100
2101static bool
2102mlxsw_sp_nexthop6_group_cmp(const struct mlxsw_sp_nexthop_group *nh_grp,
2103 const struct mlxsw_sp_fib6_entry *fib6_entry)
2104{
2105 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
2106
2107 if (nh_grp->count != fib6_entry->nrt6)
2108 return false;
2109
2110 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
2111 struct in6_addr *gw;
2112 int ifindex;
2113
2114 ifindex = mlxsw_sp_rt6->rt->dst.dev->ifindex;
2115 gw = &mlxsw_sp_rt6->rt->rt6i_gateway;
2116 if (!mlxsw_sp_nexthop6_group_has_nexthop(nh_grp, gw, ifindex))
2117 return false;
2118 }
2119
2120 return true;
2121}
2122
ba31d366
AS
2123static int
2124mlxsw_sp_nexthop_group_cmp(struct rhashtable_compare_arg *arg, const void *ptr)
2125{
2126 const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = arg->key;
2127 const struct mlxsw_sp_nexthop_group *nh_grp = ptr;
2128
e6f3b379
AS
2129 switch (cmp_arg->proto) {
2130 case MLXSW_SP_L3_PROTO_IPV4:
2131 return cmp_arg->fi != mlxsw_sp_nexthop4_group_fi(nh_grp);
2132 case MLXSW_SP_L3_PROTO_IPV6:
2133 return !mlxsw_sp_nexthop6_group_cmp(nh_grp,
2134 cmp_arg->fib6_entry);
2135 default:
2136 WARN_ON(1);
2137 return 1;
2138 }
2139}
2140
2141static int
2142mlxsw_sp_nexthop_group_type(const struct mlxsw_sp_nexthop_group *nh_grp)
2143{
2144 return nh_grp->neigh_tbl->family;
ba31d366
AS
2145}
2146
2147static u32 mlxsw_sp_nexthop_group_hash_obj(const void *data, u32 len, u32 seed)
2148{
2149 const struct mlxsw_sp_nexthop_group *nh_grp = data;
e6f3b379
AS
2150 const struct mlxsw_sp_nexthop *nh;
2151 struct fib_info *fi;
2152 unsigned int val;
2153 int i;
ba31d366 2154
e6f3b379
AS
2155 switch (mlxsw_sp_nexthop_group_type(nh_grp)) {
2156 case AF_INET:
2157 fi = mlxsw_sp_nexthop4_group_fi(nh_grp);
2158 return jhash(&fi, sizeof(fi), seed);
2159 case AF_INET6:
2160 val = nh_grp->count;
2161 for (i = 0; i < nh_grp->count; i++) {
2162 nh = &nh_grp->nexthops[i];
2163 val ^= nh->ifindex;
2164 }
2165 return jhash(&val, sizeof(val), seed);
2166 default:
2167 WARN_ON(1);
2168 return 0;
2169 }
2170}
2171
2172static u32
2173mlxsw_sp_nexthop6_group_hash(struct mlxsw_sp_fib6_entry *fib6_entry, u32 seed)
2174{
2175 unsigned int val = fib6_entry->nrt6;
2176 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
2177 struct net_device *dev;
2178
2179 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
2180 dev = mlxsw_sp_rt6->rt->dst.dev;
2181 val ^= dev->ifindex;
2182 }
2183
2184 return jhash(&val, sizeof(val), seed);
ba31d366
AS
2185}
2186
2187static u32
2188mlxsw_sp_nexthop_group_hash(const void *data, u32 len, u32 seed)
2189{
2190 const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = data;
2191
e6f3b379
AS
2192 switch (cmp_arg->proto) {
2193 case MLXSW_SP_L3_PROTO_IPV4:
2194 return jhash(&cmp_arg->fi, sizeof(cmp_arg->fi), seed);
2195 case MLXSW_SP_L3_PROTO_IPV6:
2196 return mlxsw_sp_nexthop6_group_hash(cmp_arg->fib6_entry, seed);
2197 default:
2198 WARN_ON(1);
2199 return 0;
2200 }
ba31d366
AS
2201}
2202
e9ad5e7d 2203static const struct rhashtable_params mlxsw_sp_nexthop_group_ht_params = {
e9ad5e7d 2204 .head_offset = offsetof(struct mlxsw_sp_nexthop_group, ht_node),
ba31d366
AS
2205 .hashfn = mlxsw_sp_nexthop_group_hash,
2206 .obj_hashfn = mlxsw_sp_nexthop_group_hash_obj,
2207 .obj_cmpfn = mlxsw_sp_nexthop_group_cmp,
e9ad5e7d
IS
2208};
2209
2210static int mlxsw_sp_nexthop_group_insert(struct mlxsw_sp *mlxsw_sp,
2211 struct mlxsw_sp_nexthop_group *nh_grp)
2212{
e6f3b379
AS
2213 if (mlxsw_sp_nexthop_group_type(nh_grp) == AF_INET6 &&
2214 !nh_grp->gateway)
2215 return 0;
2216
9011b677 2217 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_group_ht,
e9ad5e7d
IS
2218 &nh_grp->ht_node,
2219 mlxsw_sp_nexthop_group_ht_params);
2220}
2221
2222static void mlxsw_sp_nexthop_group_remove(struct mlxsw_sp *mlxsw_sp,
2223 struct mlxsw_sp_nexthop_group *nh_grp)
2224{
e6f3b379
AS
2225 if (mlxsw_sp_nexthop_group_type(nh_grp) == AF_INET6 &&
2226 !nh_grp->gateway)
2227 return;
2228
9011b677 2229 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_group_ht,
e9ad5e7d
IS
2230 &nh_grp->ht_node,
2231 mlxsw_sp_nexthop_group_ht_params);
2232}
2233
2234static struct mlxsw_sp_nexthop_group *
ba31d366
AS
2235mlxsw_sp_nexthop4_group_lookup(struct mlxsw_sp *mlxsw_sp,
2236 struct fib_info *fi)
e9ad5e7d 2237{
ba31d366
AS
2238 struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg;
2239
e6f3b379 2240 cmp_arg.proto = MLXSW_SP_L3_PROTO_IPV4;
ba31d366
AS
2241 cmp_arg.fi = fi;
2242 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht,
2243 &cmp_arg,
e9ad5e7d
IS
2244 mlxsw_sp_nexthop_group_ht_params);
2245}
2246
e6f3b379
AS
2247static struct mlxsw_sp_nexthop_group *
2248mlxsw_sp_nexthop6_group_lookup(struct mlxsw_sp *mlxsw_sp,
2249 struct mlxsw_sp_fib6_entry *fib6_entry)
2250{
2251 struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg;
2252
2253 cmp_arg.proto = MLXSW_SP_L3_PROTO_IPV6;
2254 cmp_arg.fib6_entry = fib6_entry;
2255 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht,
2256 &cmp_arg,
2257 mlxsw_sp_nexthop_group_ht_params);
2258}
2259
c53b8e1b
IS
2260static const struct rhashtable_params mlxsw_sp_nexthop_ht_params = {
2261 .key_offset = offsetof(struct mlxsw_sp_nexthop, key),
2262 .head_offset = offsetof(struct mlxsw_sp_nexthop, ht_node),
2263 .key_len = sizeof(struct mlxsw_sp_nexthop_key),
2264};
2265
2266static int mlxsw_sp_nexthop_insert(struct mlxsw_sp *mlxsw_sp,
2267 struct mlxsw_sp_nexthop *nh)
2268{
9011b677 2269 return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_ht,
c53b8e1b
IS
2270 &nh->ht_node, mlxsw_sp_nexthop_ht_params);
2271}
2272
2273static void mlxsw_sp_nexthop_remove(struct mlxsw_sp *mlxsw_sp,
2274 struct mlxsw_sp_nexthop *nh)
2275{
9011b677 2276 rhashtable_remove_fast(&mlxsw_sp->router->nexthop_ht, &nh->ht_node,
c53b8e1b
IS
2277 mlxsw_sp_nexthop_ht_params);
2278}
2279
ad178c8e
IS
2280static struct mlxsw_sp_nexthop *
2281mlxsw_sp_nexthop_lookup(struct mlxsw_sp *mlxsw_sp,
2282 struct mlxsw_sp_nexthop_key key)
2283{
9011b677 2284 return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_ht, &key,
ad178c8e
IS
2285 mlxsw_sp_nexthop_ht_params);
2286}
2287
a7ff87ac 2288static int mlxsw_sp_adj_index_mass_update_vr(struct mlxsw_sp *mlxsw_sp,
76610ebb 2289 const struct mlxsw_sp_fib *fib,
a7ff87ac
JP
2290 u32 adj_index, u16 ecmp_size,
2291 u32 new_adj_index,
2292 u16 new_ecmp_size)
2293{
2294 char raleu_pl[MLXSW_REG_RALEU_LEN];
2295
1a9234e6 2296 mlxsw_reg_raleu_pack(raleu_pl,
76610ebb
IS
2297 (enum mlxsw_reg_ralxx_protocol) fib->proto,
2298 fib->vr->id, adj_index, ecmp_size, new_adj_index,
1a9234e6 2299 new_ecmp_size);
a7ff87ac
JP
2300 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raleu), raleu_pl);
2301}
2302
2303static int mlxsw_sp_adj_index_mass_update(struct mlxsw_sp *mlxsw_sp,
2304 struct mlxsw_sp_nexthop_group *nh_grp,
2305 u32 old_adj_index, u16 old_ecmp_size)
2306{
2307 struct mlxsw_sp_fib_entry *fib_entry;
76610ebb 2308 struct mlxsw_sp_fib *fib = NULL;
a7ff87ac
JP
2309 int err;
2310
2311 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
76610ebb 2312 if (fib == fib_entry->fib_node->fib)
a7ff87ac 2313 continue;
76610ebb
IS
2314 fib = fib_entry->fib_node->fib;
2315 err = mlxsw_sp_adj_index_mass_update_vr(mlxsw_sp, fib,
a7ff87ac
JP
2316 old_adj_index,
2317 old_ecmp_size,
2318 nh_grp->adj_index,
2319 nh_grp->ecmp_size);
2320 if (err)
2321 return err;
2322 }
2323 return 0;
2324}
2325
2326static int mlxsw_sp_nexthop_mac_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index,
2327 struct mlxsw_sp_nexthop *nh)
2328{
2329 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
2330 char ratr_pl[MLXSW_REG_RATR_LEN];
2331
2332 mlxsw_reg_ratr_pack(ratr_pl, MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY,
89e41982
PM
2333 true, MLXSW_REG_RATR_TYPE_ETHERNET,
2334 adj_index, neigh_entry->rif);
a7ff87ac
JP
2335 mlxsw_reg_ratr_eth_entry_pack(ratr_pl, neigh_entry->ha);
2336 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl);
2337}
2338
1012b9ac
PM
2339static int mlxsw_sp_nexthop_ipip_update(struct mlxsw_sp *mlxsw_sp,
2340 u32 adj_index,
2341 struct mlxsw_sp_nexthop *nh)
2342{
2343 const struct mlxsw_sp_ipip_ops *ipip_ops;
2344
2345 ipip_ops = mlxsw_sp->router->ipip_ops_arr[nh->ipip_entry->ipipt];
2346 return ipip_ops->nexthop_update(mlxsw_sp, adj_index, nh->ipip_entry);
2347}
2348
a7ff87ac 2349static int
35225e47
PM
2350mlxsw_sp_nexthop_group_update(struct mlxsw_sp *mlxsw_sp,
2351 struct mlxsw_sp_nexthop_group *nh_grp,
2352 bool reallocate)
a7ff87ac
JP
2353{
2354 u32 adj_index = nh_grp->adj_index; /* base */
2355 struct mlxsw_sp_nexthop *nh;
2356 int i;
2357 int err;
2358
2359 for (i = 0; i < nh_grp->count; i++) {
2360 nh = &nh_grp->nexthops[i];
2361
2362 if (!nh->should_offload) {
2363 nh->offloaded = 0;
2364 continue;
2365 }
2366
a59b7e02 2367 if (nh->update || reallocate) {
35225e47
PM
2368 switch (nh->type) {
2369 case MLXSW_SP_NEXTHOP_TYPE_ETH:
2370 err = mlxsw_sp_nexthop_mac_update
2371 (mlxsw_sp, adj_index, nh);
2372 break;
1012b9ac
PM
2373 case MLXSW_SP_NEXTHOP_TYPE_IPIP:
2374 err = mlxsw_sp_nexthop_ipip_update
2375 (mlxsw_sp, adj_index, nh);
2376 break;
35225e47 2377 }
a7ff87ac
JP
2378 if (err)
2379 return err;
2380 nh->update = 0;
2381 nh->offloaded = 1;
2382 }
2383 adj_index++;
2384 }
2385 return 0;
2386}
2387
1819ae3d
IS
2388static bool
2389mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
2390 const struct mlxsw_sp_fib_entry *fib_entry);
2391
a7ff87ac
JP
2392static int
2393mlxsw_sp_nexthop_fib_entries_update(struct mlxsw_sp *mlxsw_sp,
2394 struct mlxsw_sp_nexthop_group *nh_grp)
2395{
2396 struct mlxsw_sp_fib_entry *fib_entry;
2397 int err;
2398
2399 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
1819ae3d
IS
2400 if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node,
2401 fib_entry))
2402 continue;
a7ff87ac
JP
2403 err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
2404 if (err)
2405 return err;
2406 }
2407 return 0;
2408}
2409
77d964e6
IS
2410static void
2411mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
2412 enum mlxsw_reg_ralue_op op, int err);
2413
2414static void
2415mlxsw_sp_nexthop_fib_entries_refresh(struct mlxsw_sp_nexthop_group *nh_grp)
2416{
2417 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_WRITE;
2418 struct mlxsw_sp_fib_entry *fib_entry;
2419
2420 list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
2421 if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node,
2422 fib_entry))
2423 continue;
2424 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
2425 }
2426}
2427
a7ff87ac
JP
2428static void
2429mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp,
2430 struct mlxsw_sp_nexthop_group *nh_grp)
2431{
2432 struct mlxsw_sp_nexthop *nh;
2433 bool offload_change = false;
2434 u32 adj_index;
2435 u16 ecmp_size = 0;
2436 bool old_adj_index_valid;
2437 u32 old_adj_index;
2438 u16 old_ecmp_size;
a7ff87ac
JP
2439 int i;
2440 int err;
2441
b3e8d1eb
IS
2442 if (!nh_grp->gateway) {
2443 mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
2444 return;
2445 }
2446
a7ff87ac
JP
2447 for (i = 0; i < nh_grp->count; i++) {
2448 nh = &nh_grp->nexthops[i];
2449
56b8a9ed 2450 if (nh->should_offload != nh->offloaded) {
a7ff87ac
JP
2451 offload_change = true;
2452 if (nh->should_offload)
2453 nh->update = 1;
2454 }
2455 if (nh->should_offload)
2456 ecmp_size++;
2457 }
2458 if (!offload_change) {
2459 /* Nothing was added or removed, so no need to reallocate. Just
2460 * update MAC on existing adjacency indexes.
2461 */
35225e47 2462 err = mlxsw_sp_nexthop_group_update(mlxsw_sp, nh_grp, false);
a7ff87ac
JP
2463 if (err) {
2464 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
2465 goto set_trap;
2466 }
2467 return;
2468 }
2469 if (!ecmp_size)
2470 /* No neigh of this group is connected so we just set
2471 * the trap and let everthing flow through kernel.
2472 */
2473 goto set_trap;
2474
13124443
AS
2475 err = mlxsw_sp_kvdl_alloc(mlxsw_sp, ecmp_size, &adj_index);
2476 if (err) {
a7ff87ac
JP
2477 /* We ran out of KVD linear space, just set the
2478 * trap and let everything flow through kernel.
2479 */
2480 dev_warn(mlxsw_sp->bus_info->dev, "Failed to allocate KVD linear area for nexthop group.\n");
2481 goto set_trap;
2482 }
a7ff87ac
JP
2483 old_adj_index_valid = nh_grp->adj_index_valid;
2484 old_adj_index = nh_grp->adj_index;
2485 old_ecmp_size = nh_grp->ecmp_size;
2486 nh_grp->adj_index_valid = 1;
2487 nh_grp->adj_index = adj_index;
2488 nh_grp->ecmp_size = ecmp_size;
35225e47 2489 err = mlxsw_sp_nexthop_group_update(mlxsw_sp, nh_grp, true);
a7ff87ac
JP
2490 if (err) {
2491 dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
2492 goto set_trap;
2493 }
2494
2495 if (!old_adj_index_valid) {
2496 /* The trap was set for fib entries, so we have to call
2497 * fib entry update to unset it and use adjacency index.
2498 */
2499 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
2500 if (err) {
2501 dev_warn(mlxsw_sp->bus_info->dev, "Failed to add adjacency index to fib entries.\n");
2502 goto set_trap;
2503 }
2504 return;
2505 }
2506
2507 err = mlxsw_sp_adj_index_mass_update(mlxsw_sp, nh_grp,
2508 old_adj_index, old_ecmp_size);
2509 mlxsw_sp_kvdl_free(mlxsw_sp, old_adj_index);
2510 if (err) {
2511 dev_warn(mlxsw_sp->bus_info->dev, "Failed to mass-update adjacency index for nexthop group.\n");
2512 goto set_trap;
2513 }
77d964e6
IS
2514
2515 /* Offload state within the group changed, so update the flags. */
2516 mlxsw_sp_nexthop_fib_entries_refresh(nh_grp);
2517
a7ff87ac
JP
2518 return;
2519
2520set_trap:
2521 old_adj_index_valid = nh_grp->adj_index_valid;
2522 nh_grp->adj_index_valid = 0;
2523 for (i = 0; i < nh_grp->count; i++) {
2524 nh = &nh_grp->nexthops[i];
2525 nh->offloaded = 0;
2526 }
2527 err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
2528 if (err)
2529 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set traps for fib entries.\n");
2530 if (old_adj_index_valid)
2531 mlxsw_sp_kvdl_free(mlxsw_sp, nh_grp->adj_index);
2532}
2533
2534static void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh,
2535 bool removing)
2536{
213666a3 2537 if (!removing)
a7ff87ac 2538 nh->should_offload = 1;
213666a3 2539 else if (nh->offloaded)
a7ff87ac
JP
2540 nh->should_offload = 0;
2541 nh->update = 1;
2542}
2543
2544static void
2545mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
2546 struct mlxsw_sp_neigh_entry *neigh_entry,
2547 bool removing)
2548{
2549 struct mlxsw_sp_nexthop *nh;
2550
a7ff87ac
JP
2551 list_for_each_entry(nh, &neigh_entry->nexthop_list,
2552 neigh_list_node) {
2553 __mlxsw_sp_nexthop_neigh_update(nh, removing);
2554 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2555 }
a7ff87ac
JP
2556}
2557
9665b745 2558static void mlxsw_sp_nexthop_rif_init(struct mlxsw_sp_nexthop *nh,
bf95233e 2559 struct mlxsw_sp_rif *rif)
9665b745 2560{
bf95233e 2561 if (nh->rif)
9665b745
IS
2562 return;
2563
bf95233e
AS
2564 nh->rif = rif;
2565 list_add(&nh->rif_list_node, &rif->nexthop_list);
9665b745
IS
2566}
2567
2568static void mlxsw_sp_nexthop_rif_fini(struct mlxsw_sp_nexthop *nh)
2569{
bf95233e 2570 if (!nh->rif)
9665b745
IS
2571 return;
2572
2573 list_del(&nh->rif_list_node);
bf95233e 2574 nh->rif = NULL;
9665b745
IS
2575}
2576
a8c97014
IS
2577static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp,
2578 struct mlxsw_sp_nexthop *nh)
a7ff87ac
JP
2579{
2580 struct mlxsw_sp_neigh_entry *neigh_entry;
a7ff87ac 2581 struct neighbour *n;
93a87e5e 2582 u8 nud_state, dead;
c53b8e1b
IS
2583 int err;
2584
ad178c8e 2585 if (!nh->nh_grp->gateway || nh->neigh_entry)
b8399a1e
IS
2586 return 0;
2587
33b1341c 2588 /* Take a reference of neigh here ensuring that neigh would
8de3c178 2589 * not be destructed before the nexthop entry is finished.
33b1341c 2590 * The reference is taken either in neigh_lookup() or
fd76d910 2591 * in neigh_create() in case n is not found.
33b1341c 2592 */
58adf2c4 2593 n = neigh_lookup(nh->nh_grp->neigh_tbl, &nh->gw_addr, nh->rif->dev);
33b1341c 2594 if (!n) {
58adf2c4
IS
2595 n = neigh_create(nh->nh_grp->neigh_tbl, &nh->gw_addr,
2596 nh->rif->dev);
a8c97014
IS
2597 if (IS_ERR(n))
2598 return PTR_ERR(n);
a7ff87ac 2599 neigh_event_send(n, NULL);
33b1341c
JP
2600 }
2601 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
2602 if (!neigh_entry) {
5c8802f1
IS
2603 neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
2604 if (IS_ERR(neigh_entry)) {
c53b8e1b
IS
2605 err = -EINVAL;
2606 goto err_neigh_entry_create;
5c8802f1 2607 }
a7ff87ac 2608 }
b2157149
YG
2609
2610 /* If that is the first nexthop connected to that neigh, add to
2611 * nexthop_neighs_list
2612 */
2613 if (list_empty(&neigh_entry->nexthop_list))
2614 list_add_tail(&neigh_entry->nexthop_neighs_list_node,
9011b677 2615 &mlxsw_sp->router->nexthop_neighs_list);
b2157149 2616
a7ff87ac
JP
2617 nh->neigh_entry = neigh_entry;
2618 list_add_tail(&nh->neigh_list_node, &neigh_entry->nexthop_list);
2619 read_lock_bh(&n->lock);
2620 nud_state = n->nud_state;
93a87e5e 2621 dead = n->dead;
a7ff87ac 2622 read_unlock_bh(&n->lock);
93a87e5e 2623 __mlxsw_sp_nexthop_neigh_update(nh, !(nud_state & NUD_VALID && !dead));
a7ff87ac
JP
2624
2625 return 0;
c53b8e1b
IS
2626
2627err_neigh_entry_create:
2628 neigh_release(n);
c53b8e1b 2629 return err;
a7ff87ac
JP
2630}
2631
a8c97014
IS
2632static void mlxsw_sp_nexthop_neigh_fini(struct mlxsw_sp *mlxsw_sp,
2633 struct mlxsw_sp_nexthop *nh)
a7ff87ac
JP
2634{
2635 struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
a8c97014 2636 struct neighbour *n;
a7ff87ac 2637
b8399a1e 2638 if (!neigh_entry)
a8c97014
IS
2639 return;
2640 n = neigh_entry->key.n;
b8399a1e 2641
58312125 2642 __mlxsw_sp_nexthop_neigh_update(nh, true);
a7ff87ac 2643 list_del(&nh->neigh_list_node);
e58be79e 2644 nh->neigh_entry = NULL;
b2157149
YG
2645
2646 /* If that is the last nexthop connected to that neigh, remove from
2647 * nexthop_neighs_list
2648 */
e58be79e
IS
2649 if (list_empty(&neigh_entry->nexthop_list))
2650 list_del(&neigh_entry->nexthop_neighs_list_node);
b2157149 2651
5c8802f1
IS
2652 if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
2653 mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
2654
2655 neigh_release(n);
a8c97014 2656}
c53b8e1b 2657
6ddb7426
PM
2658static bool mlxsw_sp_netdev_ipip_type(const struct mlxsw_sp *mlxsw_sp,
2659 const struct net_device *dev,
2660 enum mlxsw_sp_ipip_type *p_type)
2661{
2662 struct mlxsw_sp_router *router = mlxsw_sp->router;
2663 const struct mlxsw_sp_ipip_ops *ipip_ops;
2664 enum mlxsw_sp_ipip_type ipipt;
2665
2666 for (ipipt = 0; ipipt < MLXSW_SP_IPIP_TYPE_MAX; ++ipipt) {
2667 ipip_ops = router->ipip_ops_arr[ipipt];
2668 if (dev->type == ipip_ops->dev_type) {
2669 if (p_type)
2670 *p_type = ipipt;
2671 return true;
2672 }
2673 }
2674 return false;
2675}
2676
1012b9ac
PM
2677static int mlxsw_sp_nexthop_ipip_init(struct mlxsw_sp *mlxsw_sp,
2678 enum mlxsw_sp_ipip_type ipipt,
2679 struct mlxsw_sp_nexthop *nh,
2680 struct net_device *ol_dev)
2681{
2682 if (!nh->nh_grp->gateway || nh->ipip_entry)
2683 return 0;
2684
2685 nh->ipip_entry = mlxsw_sp_ipip_entry_get(mlxsw_sp, ipipt, ol_dev);
2686 if (IS_ERR(nh->ipip_entry))
2687 return PTR_ERR(nh->ipip_entry);
2688
2689 __mlxsw_sp_nexthop_neigh_update(nh, false);
2690 return 0;
2691}
2692
2693static void mlxsw_sp_nexthop_ipip_fini(struct mlxsw_sp *mlxsw_sp,
2694 struct mlxsw_sp_nexthop *nh)
2695{
2696 struct mlxsw_sp_ipip_entry *ipip_entry = nh->ipip_entry;
2697
2698 if (!ipip_entry)
2699 return;
2700
2701 __mlxsw_sp_nexthop_neigh_update(nh, true);
2702 mlxsw_sp_ipip_entry_put(mlxsw_sp, ipip_entry);
2703 nh->ipip_entry = NULL;
2704}
2705
2706static bool mlxsw_sp_nexthop4_ipip_type(const struct mlxsw_sp *mlxsw_sp,
2707 const struct fib_nh *fib_nh,
2708 enum mlxsw_sp_ipip_type *p_ipipt)
2709{
2710 struct net_device *dev = fib_nh->nh_dev;
2711
2712 return dev &&
2713 fib_nh->nh_parent->fib_type == RTN_UNICAST &&
2714 mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, p_ipipt);
2715}
2716
35225e47
PM
2717static void mlxsw_sp_nexthop_type_fini(struct mlxsw_sp *mlxsw_sp,
2718 struct mlxsw_sp_nexthop *nh)
2719{
2720 switch (nh->type) {
2721 case MLXSW_SP_NEXTHOP_TYPE_ETH:
2722 mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
2723 mlxsw_sp_nexthop_rif_fini(nh);
2724 break;
1012b9ac
PM
2725 case MLXSW_SP_NEXTHOP_TYPE_IPIP:
2726 mlxsw_sp_nexthop_ipip_fini(mlxsw_sp, nh);
2727 break;
35225e47
PM
2728 }
2729}
2730
2731static int mlxsw_sp_nexthop4_type_init(struct mlxsw_sp *mlxsw_sp,
2732 struct mlxsw_sp_nexthop *nh,
2733 struct fib_nh *fib_nh)
2734{
1012b9ac 2735 struct mlxsw_sp_router *router = mlxsw_sp->router;
35225e47 2736 struct net_device *dev = fib_nh->nh_dev;
1012b9ac 2737 enum mlxsw_sp_ipip_type ipipt;
35225e47
PM
2738 struct mlxsw_sp_rif *rif;
2739 int err;
2740
1012b9ac
PM
2741 if (mlxsw_sp_nexthop4_ipip_type(mlxsw_sp, fib_nh, &ipipt) &&
2742 router->ipip_ops_arr[ipipt]->can_offload(mlxsw_sp, dev,
2743 MLXSW_SP_L3_PROTO_IPV4)) {
2744 nh->type = MLXSW_SP_NEXTHOP_TYPE_IPIP;
2745 return mlxsw_sp_nexthop_ipip_init(mlxsw_sp, ipipt, nh, dev);
2746 }
2747
35225e47
PM
2748 nh->type = MLXSW_SP_NEXTHOP_TYPE_ETH;
2749 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
2750 if (!rif)
2751 return 0;
2752
2753 mlxsw_sp_nexthop_rif_init(nh, rif);
2754 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
2755 if (err)
2756 goto err_neigh_init;
2757
2758 return 0;
2759
2760err_neigh_init:
2761 mlxsw_sp_nexthop_rif_fini(nh);
2762 return err;
2763}
2764
2765static void mlxsw_sp_nexthop4_type_fini(struct mlxsw_sp *mlxsw_sp,
2766 struct mlxsw_sp_nexthop *nh)
2767{
2768 mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);
2769}
2770
0e6ea2a4
IS
2771static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp,
2772 struct mlxsw_sp_nexthop_group *nh_grp,
2773 struct mlxsw_sp_nexthop *nh,
2774 struct fib_nh *fib_nh)
a8c97014
IS
2775{
2776 struct net_device *dev = fib_nh->nh_dev;
df6dd79b 2777 struct in_device *in_dev;
a8c97014
IS
2778 int err;
2779
2780 nh->nh_grp = nh_grp;
2781 nh->key.fib_nh = fib_nh;
58adf2c4 2782 memcpy(&nh->gw_addr, &fib_nh->nh_gw, sizeof(fib_nh->nh_gw));
a8c97014
IS
2783 err = mlxsw_sp_nexthop_insert(mlxsw_sp, nh);
2784 if (err)
2785 return err;
2786
97989ee0
IS
2787 if (!dev)
2788 return 0;
2789
df6dd79b
IS
2790 in_dev = __in_dev_get_rtnl(dev);
2791 if (in_dev && IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) &&
2792 fib_nh->nh_flags & RTNH_F_LINKDOWN)
2793 return 0;
2794
35225e47 2795 err = mlxsw_sp_nexthop4_type_init(mlxsw_sp, nh, fib_nh);
a8c97014
IS
2796 if (err)
2797 goto err_nexthop_neigh_init;
2798
2799 return 0;
2800
2801err_nexthop_neigh_init:
2802 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
2803 return err;
2804}
2805
0e6ea2a4
IS
2806static void mlxsw_sp_nexthop4_fini(struct mlxsw_sp *mlxsw_sp,
2807 struct mlxsw_sp_nexthop *nh)
a8c97014 2808{
35225e47 2809 mlxsw_sp_nexthop4_type_fini(mlxsw_sp, nh);
c53b8e1b 2810 mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
a7ff87ac
JP
2811}
2812
0e6ea2a4
IS
2813static void mlxsw_sp_nexthop4_event(struct mlxsw_sp *mlxsw_sp,
2814 unsigned long event, struct fib_nh *fib_nh)
ad178c8e
IS
2815{
2816 struct mlxsw_sp_nexthop_key key;
2817 struct mlxsw_sp_nexthop *nh;
ad178c8e 2818
9011b677 2819 if (mlxsw_sp->router->aborted)
ad178c8e
IS
2820 return;
2821
2822 key.fib_nh = fib_nh;
2823 nh = mlxsw_sp_nexthop_lookup(mlxsw_sp, key);
2824 if (WARN_ON_ONCE(!nh))
2825 return;
2826
ad178c8e
IS
2827 switch (event) {
2828 case FIB_EVENT_NH_ADD:
35225e47 2829 mlxsw_sp_nexthop4_type_init(mlxsw_sp, nh, fib_nh);
ad178c8e
IS
2830 break;
2831 case FIB_EVENT_NH_DEL:
35225e47 2832 mlxsw_sp_nexthop4_type_fini(mlxsw_sp, nh);
ad178c8e
IS
2833 break;
2834 }
2835
2836 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2837}
2838
9665b745 2839static void mlxsw_sp_nexthop_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
bf95233e 2840 struct mlxsw_sp_rif *rif)
9665b745
IS
2841{
2842 struct mlxsw_sp_nexthop *nh, *tmp;
2843
bf95233e 2844 list_for_each_entry_safe(nh, tmp, &rif->nexthop_list, rif_list_node) {
35225e47 2845 mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);
9665b745
IS
2846 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
2847 }
2848}
2849
9b01451a
PM
2850static bool mlxsw_sp_fi_is_gateway(const struct mlxsw_sp *mlxsw_sp,
2851 const struct fib_info *fi)
2852{
1012b9ac
PM
2853 return fi->fib_nh->nh_scope == RT_SCOPE_LINK ||
2854 mlxsw_sp_nexthop4_ipip_type(mlxsw_sp, fi->fib_nh, NULL);
9b01451a
PM
2855}
2856
a7ff87ac 2857static struct mlxsw_sp_nexthop_group *
0e6ea2a4 2858mlxsw_sp_nexthop4_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi)
a7ff87ac
JP
2859{
2860 struct mlxsw_sp_nexthop_group *nh_grp;
2861 struct mlxsw_sp_nexthop *nh;
2862 struct fib_nh *fib_nh;
2863 size_t alloc_size;
2864 int i;
2865 int err;
2866
2867 alloc_size = sizeof(*nh_grp) +
2868 fi->fib_nhs * sizeof(struct mlxsw_sp_nexthop);
2869 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
2870 if (!nh_grp)
2871 return ERR_PTR(-ENOMEM);
ba31d366 2872 nh_grp->priv = fi;
a7ff87ac 2873 INIT_LIST_HEAD(&nh_grp->fib_list);
58adf2c4
IS
2874 nh_grp->neigh_tbl = &arp_tbl;
2875
9b01451a 2876 nh_grp->gateway = mlxsw_sp_fi_is_gateway(mlxsw_sp, fi);
a7ff87ac 2877 nh_grp->count = fi->fib_nhs;
7387dbbc 2878 fib_info_hold(fi);
a7ff87ac
JP
2879 for (i = 0; i < nh_grp->count; i++) {
2880 nh = &nh_grp->nexthops[i];
2881 fib_nh = &fi->fib_nh[i];
0e6ea2a4 2882 err = mlxsw_sp_nexthop4_init(mlxsw_sp, nh_grp, nh, fib_nh);
a7ff87ac 2883 if (err)
0e6ea2a4 2884 goto err_nexthop4_init;
a7ff87ac 2885 }
e9ad5e7d
IS
2886 err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
2887 if (err)
2888 goto err_nexthop_group_insert;
a7ff87ac
JP
2889 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
2890 return nh_grp;
2891
e9ad5e7d 2892err_nexthop_group_insert:
0e6ea2a4 2893err_nexthop4_init:
df6dd79b
IS
2894 for (i--; i >= 0; i--) {
2895 nh = &nh_grp->nexthops[i];
0e6ea2a4 2896 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
df6dd79b 2897 }
ba31d366 2898 fib_info_put(fi);
a7ff87ac
JP
2899 kfree(nh_grp);
2900 return ERR_PTR(err);
2901}
2902
2903static void
0e6ea2a4
IS
2904mlxsw_sp_nexthop4_group_destroy(struct mlxsw_sp *mlxsw_sp,
2905 struct mlxsw_sp_nexthop_group *nh_grp)
a7ff87ac
JP
2906{
2907 struct mlxsw_sp_nexthop *nh;
2908 int i;
2909
e9ad5e7d 2910 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
a7ff87ac
JP
2911 for (i = 0; i < nh_grp->count; i++) {
2912 nh = &nh_grp->nexthops[i];
0e6ea2a4 2913 mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
a7ff87ac 2914 }
58312125
IS
2915 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
2916 WARN_ON_ONCE(nh_grp->adj_index_valid);
ba31d366 2917 fib_info_put(mlxsw_sp_nexthop4_group_fi(nh_grp));
a7ff87ac
JP
2918 kfree(nh_grp);
2919}
2920
0e6ea2a4
IS
2921static int mlxsw_sp_nexthop4_group_get(struct mlxsw_sp *mlxsw_sp,
2922 struct mlxsw_sp_fib_entry *fib_entry,
2923 struct fib_info *fi)
a7ff87ac
JP
2924{
2925 struct mlxsw_sp_nexthop_group *nh_grp;
2926
ba31d366 2927 nh_grp = mlxsw_sp_nexthop4_group_lookup(mlxsw_sp, fi);
a7ff87ac 2928 if (!nh_grp) {
0e6ea2a4 2929 nh_grp = mlxsw_sp_nexthop4_group_create(mlxsw_sp, fi);
a7ff87ac
JP
2930 if (IS_ERR(nh_grp))
2931 return PTR_ERR(nh_grp);
2932 }
2933 list_add_tail(&fib_entry->nexthop_group_node, &nh_grp->fib_list);
2934 fib_entry->nh_group = nh_grp;
2935 return 0;
2936}
2937
0e6ea2a4
IS
2938static void mlxsw_sp_nexthop4_group_put(struct mlxsw_sp *mlxsw_sp,
2939 struct mlxsw_sp_fib_entry *fib_entry)
a7ff87ac
JP
2940{
2941 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
2942
2943 list_del(&fib_entry->nexthop_group_node);
2944 if (!list_empty(&nh_grp->fib_list))
2945 return;
0e6ea2a4 2946 mlxsw_sp_nexthop4_group_destroy(mlxsw_sp, nh_grp);
a7ff87ac
JP
2947}
2948
4f1c7f1f
IS
2949static bool
2950mlxsw_sp_fib4_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
2951{
2952 struct mlxsw_sp_fib4_entry *fib4_entry;
2953
2954 fib4_entry = container_of(fib_entry, struct mlxsw_sp_fib4_entry,
2955 common);
2956 return !fib4_entry->tos;
2957}
2958
013b20f9
IS
2959static bool
2960mlxsw_sp_fib_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
2961{
2962 struct mlxsw_sp_nexthop_group *nh_group = fib_entry->nh_group;
2963
4f1c7f1f
IS
2964 switch (fib_entry->fib_node->fib->proto) {
2965 case MLXSW_SP_L3_PROTO_IPV4:
2966 if (!mlxsw_sp_fib4_entry_should_offload(fib_entry))
2967 return false;
2968 break;
2969 case MLXSW_SP_L3_PROTO_IPV6:
2970 break;
2971 }
9aecce1c 2972
013b20f9
IS
2973 switch (fib_entry->type) {
2974 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
2975 return !!nh_group->adj_index_valid;
2976 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
70ad3506 2977 return !!nh_group->nh_rif;
4607f6d2
PM
2978 case MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP:
2979 return true;
013b20f9
IS
2980 default:
2981 return false;
2982 }
2983}
2984
428b851f
IS
2985static struct mlxsw_sp_nexthop *
2986mlxsw_sp_rt6_nexthop(struct mlxsw_sp_nexthop_group *nh_grp,
2987 const struct mlxsw_sp_rt6 *mlxsw_sp_rt6)
2988{
2989 int i;
2990
2991 for (i = 0; i < nh_grp->count; i++) {
2992 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
2993 struct rt6_info *rt = mlxsw_sp_rt6->rt;
2994
2995 if (nh->rif && nh->rif->dev == rt->dst.dev &&
2996 ipv6_addr_equal((const struct in6_addr *) &nh->gw_addr,
2997 &rt->rt6i_gateway))
2998 return nh;
2999 continue;
3000 }
3001
3002 return NULL;
3003}
3004
3984d1a8
IS
3005static void
3006mlxsw_sp_fib4_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
3007{
3008 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
3009 int i;
3010
4607f6d2
PM
3011 if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL ||
3012 fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP) {
3984d1a8
IS
3013 nh_grp->nexthops->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
3014 return;
3015 }
3016
3017 for (i = 0; i < nh_grp->count; i++) {
3018 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
3019
3020 if (nh->offloaded)
3021 nh->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
3022 else
3023 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
3024 }
3025}
3026
3027static void
3028mlxsw_sp_fib4_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
3029{
3030 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
3031 int i;
3032
3033 for (i = 0; i < nh_grp->count; i++) {
3034 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
3035
3036 nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD;
3037 }
3038}
3039
428b851f
IS
3040static void
3041mlxsw_sp_fib6_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
3042{
3043 struct mlxsw_sp_fib6_entry *fib6_entry;
3044 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3045
3046 fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,
3047 common);
3048
3049 if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) {
3050 list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,
fe400799 3051 list)->rt->rt6i_nh_flags |= RTNH_F_OFFLOAD;
428b851f
IS
3052 return;
3053 }
3054
3055 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
3056 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
3057 struct mlxsw_sp_nexthop *nh;
3058
3059 nh = mlxsw_sp_rt6_nexthop(nh_grp, mlxsw_sp_rt6);
3060 if (nh && nh->offloaded)
fe400799 3061 mlxsw_sp_rt6->rt->rt6i_nh_flags |= RTNH_F_OFFLOAD;
428b851f 3062 else
fe400799 3063 mlxsw_sp_rt6->rt->rt6i_nh_flags &= ~RTNH_F_OFFLOAD;
428b851f
IS
3064 }
3065}
3066
3067static void
3068mlxsw_sp_fib6_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
3069{
3070 struct mlxsw_sp_fib6_entry *fib6_entry;
3071 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3072
3073 fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,
3074 common);
3075 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
3076 struct rt6_info *rt = mlxsw_sp_rt6->rt;
3077
fe400799 3078 rt->rt6i_nh_flags &= ~RTNH_F_OFFLOAD;
428b851f
IS
3079 }
3080}
3081
013b20f9
IS
3082static void mlxsw_sp_fib_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
3083{
76610ebb 3084 switch (fib_entry->fib_node->fib->proto) {
013b20f9 3085 case MLXSW_SP_L3_PROTO_IPV4:
3984d1a8 3086 mlxsw_sp_fib4_entry_offload_set(fib_entry);
013b20f9
IS
3087 break;
3088 case MLXSW_SP_L3_PROTO_IPV6:
428b851f
IS
3089 mlxsw_sp_fib6_entry_offload_set(fib_entry);
3090 break;
013b20f9
IS
3091 }
3092}
3093
3094static void
3095mlxsw_sp_fib_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
3096{
76610ebb 3097 switch (fib_entry->fib_node->fib->proto) {
013b20f9 3098 case MLXSW_SP_L3_PROTO_IPV4:
3984d1a8 3099 mlxsw_sp_fib4_entry_offload_unset(fib_entry);
013b20f9
IS
3100 break;
3101 case MLXSW_SP_L3_PROTO_IPV6:
428b851f
IS
3102 mlxsw_sp_fib6_entry_offload_unset(fib_entry);
3103 break;
013b20f9 3104 }
013b20f9
IS
3105}
3106
3107static void
3108mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry,
3109 enum mlxsw_reg_ralue_op op, int err)
3110{
3111 switch (op) {
3112 case MLXSW_REG_RALUE_OP_WRITE_DELETE:
013b20f9
IS
3113 return mlxsw_sp_fib_entry_offload_unset(fib_entry);
3114 case MLXSW_REG_RALUE_OP_WRITE_WRITE:
3115 if (err)
3116 return;
1353ee70 3117 if (mlxsw_sp_fib_entry_should_offload(fib_entry))
013b20f9 3118 mlxsw_sp_fib_entry_offload_set(fib_entry);
1353ee70 3119 else if (!mlxsw_sp_fib_entry_should_offload(fib_entry))
013b20f9
IS
3120 mlxsw_sp_fib_entry_offload_unset(fib_entry);
3121 return;
3122 default:
3123 return;
3124 }
3125}
3126
9dbf4d76
IS
3127static void
3128mlxsw_sp_fib_entry_ralue_pack(char *ralue_pl,
3129 const struct mlxsw_sp_fib_entry *fib_entry,
3130 enum mlxsw_reg_ralue_op op)
a7ff87ac 3131{
76610ebb 3132 struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib;
9dbf4d76
IS
3133 enum mlxsw_reg_ralxx_protocol proto;
3134 u32 *p_dip;
3135
3136 proto = (enum mlxsw_reg_ralxx_protocol) fib->proto;
3137
3138 switch (fib->proto) {
3139 case MLXSW_SP_L3_PROTO_IPV4:
3140 p_dip = (u32 *) fib_entry->fib_node->key.addr;
3141 mlxsw_reg_ralue_pack4(ralue_pl, proto, op, fib->vr->id,
3142 fib_entry->fib_node->key.prefix_len,
3143 *p_dip);
3144 break;
3145 case MLXSW_SP_L3_PROTO_IPV6:
3146 mlxsw_reg_ralue_pack6(ralue_pl, proto, op, fib->vr->id,
3147 fib_entry->fib_node->key.prefix_len,
3148 fib_entry->fib_node->key.addr);
3149 break;
3150 }
3151}
3152
3153static int mlxsw_sp_fib_entry_op_remote(struct mlxsw_sp *mlxsw_sp,
3154 struct mlxsw_sp_fib_entry *fib_entry,
3155 enum mlxsw_reg_ralue_op op)
3156{
3157 char ralue_pl[MLXSW_REG_RALUE_LEN];
a7ff87ac
JP
3158 enum mlxsw_reg_ralue_trap_action trap_action;
3159 u16 trap_id = 0;
3160 u32 adjacency_index = 0;
3161 u16 ecmp_size = 0;
3162
3163 /* In case the nexthop group adjacency index is valid, use it
3164 * with provided ECMP size. Otherwise, setup trap and pass
3165 * traffic to kernel.
3166 */
4b411477 3167 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
a7ff87ac
JP
3168 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
3169 adjacency_index = fib_entry->nh_group->adj_index;
3170 ecmp_size = fib_entry->nh_group->ecmp_size;
3171 } else {
3172 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
3173 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
3174 }
3175
9dbf4d76 3176 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
a7ff87ac
JP
3177 mlxsw_reg_ralue_act_remote_pack(ralue_pl, trap_action, trap_id,
3178 adjacency_index, ecmp_size);
3179 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
3180}
3181
9dbf4d76
IS
3182static int mlxsw_sp_fib_entry_op_local(struct mlxsw_sp *mlxsw_sp,
3183 struct mlxsw_sp_fib_entry *fib_entry,
3184 enum mlxsw_reg_ralue_op op)
61c503f9 3185{
bf95233e 3186 struct mlxsw_sp_rif *rif = fib_entry->nh_group->nh_rif;
70ad3506 3187 enum mlxsw_reg_ralue_trap_action trap_action;
61c503f9 3188 char ralue_pl[MLXSW_REG_RALUE_LEN];
70ad3506 3189 u16 trap_id = 0;
bf95233e 3190 u16 rif_index = 0;
70ad3506
IS
3191
3192 if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
3193 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
bf95233e 3194 rif_index = rif->rif_index;
70ad3506
IS
3195 } else {
3196 trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
3197 trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
3198 }
61c503f9 3199
9dbf4d76 3200 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
bf95233e
AS
3201 mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, trap_id,
3202 rif_index);
61c503f9
JP
3203 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
3204}
3205
9dbf4d76
IS
3206static int mlxsw_sp_fib_entry_op_trap(struct mlxsw_sp *mlxsw_sp,
3207 struct mlxsw_sp_fib_entry *fib_entry,
3208 enum mlxsw_reg_ralue_op op)
61c503f9
JP
3209{
3210 char ralue_pl[MLXSW_REG_RALUE_LEN];
61c503f9 3211
9dbf4d76 3212 mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
61c503f9
JP
3213 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
3214 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
3215}
3216
4607f6d2
PM
3217static int
3218mlxsw_sp_fib_entry_op_ipip_decap(struct mlxsw_sp *mlxsw_sp,
3219 struct mlxsw_sp_fib_entry *fib_entry,
3220 enum mlxsw_reg_ralue_op op)
3221{
3222 struct mlxsw_sp_ipip_entry *ipip_entry = fib_entry->decap.ipip_entry;
3223 const struct mlxsw_sp_ipip_ops *ipip_ops;
3224
3225 if (WARN_ON(!ipip_entry))
3226 return -EINVAL;
3227
3228 ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt];
3229 return ipip_ops->fib_entry_op(mlxsw_sp, ipip_entry, op,
3230 fib_entry->decap.tunnel_index);
3231}
3232
9dbf4d76
IS
3233static int __mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
3234 struct mlxsw_sp_fib_entry *fib_entry,
3235 enum mlxsw_reg_ralue_op op)
61c503f9
JP
3236{
3237 switch (fib_entry->type) {
3238 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
9dbf4d76 3239 return mlxsw_sp_fib_entry_op_remote(mlxsw_sp, fib_entry, op);
61c503f9 3240 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
9dbf4d76 3241 return mlxsw_sp_fib_entry_op_local(mlxsw_sp, fib_entry, op);
61c503f9 3242 case MLXSW_SP_FIB_ENTRY_TYPE_TRAP:
9dbf4d76 3243 return mlxsw_sp_fib_entry_op_trap(mlxsw_sp, fib_entry, op);
4607f6d2
PM
3244 case MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP:
3245 return mlxsw_sp_fib_entry_op_ipip_decap(mlxsw_sp,
3246 fib_entry, op);
61c503f9
JP
3247 }
3248 return -EINVAL;
3249}
3250
3251static int mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
3252 struct mlxsw_sp_fib_entry *fib_entry,
3253 enum mlxsw_reg_ralue_op op)
3254{
9dbf4d76 3255 int err = __mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry, op);
013b20f9 3256
013b20f9 3257 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, err);
9dbf4d76 3258
013b20f9 3259 return err;
61c503f9
JP
3260}
3261
3262static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
3263 struct mlxsw_sp_fib_entry *fib_entry)
3264{
7146da31
JP
3265 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
3266 MLXSW_REG_RALUE_OP_WRITE_WRITE);
61c503f9
JP
3267}
3268
3269static int mlxsw_sp_fib_entry_del(struct mlxsw_sp *mlxsw_sp,
3270 struct mlxsw_sp_fib_entry *fib_entry)
3271{
3272 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
3273 MLXSW_REG_RALUE_OP_WRITE_DELETE);
3274}
3275
61c503f9 3276static int
013b20f9
IS
3277mlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp,
3278 const struct fib_entry_notifier_info *fen_info,
3279 struct mlxsw_sp_fib_entry *fib_entry)
61c503f9 3280{
4607f6d2
PM
3281 union mlxsw_sp_l3addr dip = { .addr4 = htonl(fen_info->dst) };
3282 struct net_device *dev = fen_info->fi->fib_dev;
3283 struct mlxsw_sp_ipip_entry *ipip_entry;
b45f64d1 3284 struct fib_info *fi = fen_info->fi;
61c503f9 3285
97989ee0 3286 switch (fen_info->type) {
97989ee0 3287 case RTN_LOCAL:
4607f6d2
PM
3288 ipip_entry = mlxsw_sp_ipip_entry_find_by_decap(mlxsw_sp, dev,
3289 MLXSW_SP_L3_PROTO_IPV4, dip);
3290 if (ipip_entry) {
3291 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP;
3292 return mlxsw_sp_fib_entry_decap_init(mlxsw_sp,
3293 fib_entry,
3294 ipip_entry);
3295 }
3296 /* fall through */
3297 case RTN_BROADCAST:
61c503f9
JP
3298 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
3299 return 0;
97989ee0
IS
3300 case RTN_UNREACHABLE: /* fall through */
3301 case RTN_BLACKHOLE: /* fall through */
3302 case RTN_PROHIBIT:
3303 /* Packets hitting these routes need to be trapped, but
3304 * can do so with a lower priority than packets directed
3305 * at the host, so use action type local instead of trap.
3306 */
61c503f9 3307 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
97989ee0
IS
3308 return 0;
3309 case RTN_UNICAST:
9b01451a 3310 if (mlxsw_sp_fi_is_gateway(mlxsw_sp, fi))
97989ee0 3311 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
9b01451a
PM
3312 else
3313 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
97989ee0
IS
3314 return 0;
3315 default:
3316 return -EINVAL;
3317 }
a7ff87ac
JP
3318}
3319
4f1c7f1f 3320static struct mlxsw_sp_fib4_entry *
9aecce1c
IS
3321mlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp,
3322 struct mlxsw_sp_fib_node *fib_node,
3323 const struct fib_entry_notifier_info *fen_info)
61c503f9 3324{
4f1c7f1f 3325 struct mlxsw_sp_fib4_entry *fib4_entry;
61c503f9 3326 struct mlxsw_sp_fib_entry *fib_entry;
61c503f9
JP
3327 int err;
3328
4f1c7f1f
IS
3329 fib4_entry = kzalloc(sizeof(*fib4_entry), GFP_KERNEL);
3330 if (!fib4_entry)
3331 return ERR_PTR(-ENOMEM);
3332 fib_entry = &fib4_entry->common;
61c503f9 3333
013b20f9 3334 err = mlxsw_sp_fib4_entry_type_set(mlxsw_sp, fen_info, fib_entry);
61c503f9 3335 if (err)
013b20f9 3336 goto err_fib4_entry_type_set;
61c503f9 3337
0e6ea2a4 3338 err = mlxsw_sp_nexthop4_group_get(mlxsw_sp, fib_entry, fen_info->fi);
b8399a1e 3339 if (err)
0e6ea2a4 3340 goto err_nexthop4_group_get;
b8399a1e 3341
4f1c7f1f
IS
3342 fib4_entry->prio = fen_info->fi->fib_priority;
3343 fib4_entry->tb_id = fen_info->tb_id;
3344 fib4_entry->type = fen_info->type;
3345 fib4_entry->tos = fen_info->tos;
9aecce1c
IS
3346
3347 fib_entry->fib_node = fib_node;
3348
4f1c7f1f 3349 return fib4_entry;
5b004412 3350
0e6ea2a4 3351err_nexthop4_group_get:
013b20f9 3352err_fib4_entry_type_set:
4f1c7f1f 3353 kfree(fib4_entry);
5b004412
JP
3354 return ERR_PTR(err);
3355}
3356
9aecce1c 3357static void mlxsw_sp_fib4_entry_destroy(struct mlxsw_sp *mlxsw_sp,
4f1c7f1f 3358 struct mlxsw_sp_fib4_entry *fib4_entry)
9aecce1c 3359{
0e6ea2a4 3360 mlxsw_sp_nexthop4_group_put(mlxsw_sp, &fib4_entry->common);
4f1c7f1f 3361 kfree(fib4_entry);
9aecce1c
IS
3362}
3363
4f1c7f1f 3364static struct mlxsw_sp_fib4_entry *
9aecce1c
IS
3365mlxsw_sp_fib4_entry_lookup(struct mlxsw_sp *mlxsw_sp,
3366 const struct fib_entry_notifier_info *fen_info)
5b004412 3367{
4f1c7f1f 3368 struct mlxsw_sp_fib4_entry *fib4_entry;
9aecce1c 3369 struct mlxsw_sp_fib_node *fib_node;
160e22aa
IS
3370 struct mlxsw_sp_fib *fib;
3371 struct mlxsw_sp_vr *vr;
3372
3373 vr = mlxsw_sp_vr_find(mlxsw_sp, fen_info->tb_id);
3374 if (!vr)
3375 return NULL;
3376 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV4);
5b004412 3377
160e22aa
IS
3378 fib_node = mlxsw_sp_fib_node_lookup(fib, &fen_info->dst,
3379 sizeof(fen_info->dst),
3380 fen_info->dst_len);
3381 if (!fib_node)
9aecce1c
IS
3382 return NULL;
3383
4f1c7f1f
IS
3384 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
3385 if (fib4_entry->tb_id == fen_info->tb_id &&
3386 fib4_entry->tos == fen_info->tos &&
3387 fib4_entry->type == fen_info->type &&
ba31d366
AS
3388 mlxsw_sp_nexthop4_group_fi(fib4_entry->common.nh_group) ==
3389 fen_info->fi) {
4f1c7f1f 3390 return fib4_entry;
9aecce1c
IS
3391 }
3392 }
3393
3394 return NULL;
3395}
3396
3397static const struct rhashtable_params mlxsw_sp_fib_ht_params = {
3398 .key_offset = offsetof(struct mlxsw_sp_fib_node, key),
3399 .head_offset = offsetof(struct mlxsw_sp_fib_node, ht_node),
3400 .key_len = sizeof(struct mlxsw_sp_fib_key),
3401 .automatic_shrinking = true,
3402};
3403
3404static int mlxsw_sp_fib_node_insert(struct mlxsw_sp_fib *fib,
3405 struct mlxsw_sp_fib_node *fib_node)
3406{
3407 return rhashtable_insert_fast(&fib->ht, &fib_node->ht_node,
3408 mlxsw_sp_fib_ht_params);
3409}
3410
3411static void mlxsw_sp_fib_node_remove(struct mlxsw_sp_fib *fib,
3412 struct mlxsw_sp_fib_node *fib_node)
3413{
3414 rhashtable_remove_fast(&fib->ht, &fib_node->ht_node,
3415 mlxsw_sp_fib_ht_params);
3416}
3417
3418static struct mlxsw_sp_fib_node *
3419mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
3420 size_t addr_len, unsigned char prefix_len)
3421{
3422 struct mlxsw_sp_fib_key key;
3423
3424 memset(&key, 0, sizeof(key));
3425 memcpy(key.addr, addr, addr_len);
3426 key.prefix_len = prefix_len;
3427 return rhashtable_lookup_fast(&fib->ht, &key, mlxsw_sp_fib_ht_params);
3428}
3429
3430static struct mlxsw_sp_fib_node *
76610ebb 3431mlxsw_sp_fib_node_create(struct mlxsw_sp_fib *fib, const void *addr,
9aecce1c
IS
3432 size_t addr_len, unsigned char prefix_len)
3433{
3434 struct mlxsw_sp_fib_node *fib_node;
3435
3436 fib_node = kzalloc(sizeof(*fib_node), GFP_KERNEL);
3437 if (!fib_node)
5b004412
JP
3438 return NULL;
3439
9aecce1c 3440 INIT_LIST_HEAD(&fib_node->entry_list);
76610ebb 3441 list_add(&fib_node->list, &fib->node_list);
9aecce1c
IS
3442 memcpy(fib_node->key.addr, addr, addr_len);
3443 fib_node->key.prefix_len = prefix_len;
9aecce1c
IS
3444
3445 return fib_node;
3446}
3447
3448static void mlxsw_sp_fib_node_destroy(struct mlxsw_sp_fib_node *fib_node)
3449{
9aecce1c
IS
3450 list_del(&fib_node->list);
3451 WARN_ON(!list_empty(&fib_node->entry_list));
3452 kfree(fib_node);
3453}
3454
3455static bool
3456mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
3457 const struct mlxsw_sp_fib_entry *fib_entry)
3458{
3459 return list_first_entry(&fib_node->entry_list,
3460 struct mlxsw_sp_fib_entry, list) == fib_entry;
3461}
3462
fc922bb0
IS
3463static int mlxsw_sp_fib_lpm_tree_link(struct mlxsw_sp *mlxsw_sp,
3464 struct mlxsw_sp_fib *fib,
3465 struct mlxsw_sp_fib_node *fib_node)
3466{
3467 struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } };
3468 struct mlxsw_sp_lpm_tree *lpm_tree;
3469 int err;
3470
3471 /* Since the tree is shared between all virtual routers we must
3472 * make sure it contains all the required prefix lengths. This
3473 * can be computed by either adding the new prefix length to the
3474 * existing prefix usage of a bound tree, or by aggregating the
3475 * prefix lengths across all virtual routers and adding the new
3476 * one as well.
3477 */
3478 if (fib->lpm_tree)
3479 mlxsw_sp_prefix_usage_cpy(&req_prefix_usage,
3480 &fib->lpm_tree->prefix_usage);
3481 else
3482 mlxsw_sp_vrs_prefixes(mlxsw_sp, fib->proto, &req_prefix_usage);
3483 mlxsw_sp_prefix_usage_set(&req_prefix_usage, fib_node->key.prefix_len);
3484
3485 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
3486 fib->proto);
3487 if (IS_ERR(lpm_tree))
3488 return PTR_ERR(lpm_tree);
3489
3490 if (fib->lpm_tree && fib->lpm_tree->id == lpm_tree->id)
3491 return 0;
3492
3493 err = mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree);
3494 if (err)
3495 return err;
3496
3497 return 0;
3498}
3499
3500static void mlxsw_sp_fib_lpm_tree_unlink(struct mlxsw_sp *mlxsw_sp,
3501 struct mlxsw_sp_fib *fib)
3502{
3503 struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } };
3504 struct mlxsw_sp_lpm_tree *lpm_tree;
3505
3506 /* Aggregate prefix lengths across all virtual routers to make
3507 * sure we only have used prefix lengths in the LPM tree.
3508 */
3509 mlxsw_sp_vrs_prefixes(mlxsw_sp, fib->proto, &req_prefix_usage);
3510 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
3511 fib->proto);
3512 if (IS_ERR(lpm_tree))
3513 goto err_tree_get;
3514 mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree);
3515
3516err_tree_get:
3517 if (!mlxsw_sp_prefix_usage_none(&fib->prefix_usage))
3518 return;
3519 mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, fib);
3520 mlxsw_sp_lpm_tree_put(mlxsw_sp, fib->lpm_tree);
3521 fib->lpm_tree = NULL;
3522}
3523
9aecce1c
IS
3524static void mlxsw_sp_fib_node_prefix_inc(struct mlxsw_sp_fib_node *fib_node)
3525{
3526 unsigned char prefix_len = fib_node->key.prefix_len;
76610ebb 3527 struct mlxsw_sp_fib *fib = fib_node->fib;
9aecce1c
IS
3528
3529 if (fib->prefix_ref_count[prefix_len]++ == 0)
3530 mlxsw_sp_prefix_usage_set(&fib->prefix_usage, prefix_len);
3531}
3532
3533static void mlxsw_sp_fib_node_prefix_dec(struct mlxsw_sp_fib_node *fib_node)
3534{
3535 unsigned char prefix_len = fib_node->key.prefix_len;
76610ebb 3536 struct mlxsw_sp_fib *fib = fib_node->fib;
9aecce1c
IS
3537
3538 if (--fib->prefix_ref_count[prefix_len] == 0)
3539 mlxsw_sp_prefix_usage_clear(&fib->prefix_usage, prefix_len);
5b004412
JP
3540}
3541
76610ebb
IS
3542static int mlxsw_sp_fib_node_init(struct mlxsw_sp *mlxsw_sp,
3543 struct mlxsw_sp_fib_node *fib_node,
3544 struct mlxsw_sp_fib *fib)
3545{
76610ebb
IS
3546 int err;
3547
3548 err = mlxsw_sp_fib_node_insert(fib, fib_node);
3549 if (err)
3550 return err;
3551 fib_node->fib = fib;
3552
fc922bb0
IS
3553 err = mlxsw_sp_fib_lpm_tree_link(mlxsw_sp, fib, fib_node);
3554 if (err)
3555 goto err_fib_lpm_tree_link;
76610ebb
IS
3556
3557 mlxsw_sp_fib_node_prefix_inc(fib_node);
3558
3559 return 0;
3560
fc922bb0 3561err_fib_lpm_tree_link:
76610ebb
IS
3562 fib_node->fib = NULL;
3563 mlxsw_sp_fib_node_remove(fib, fib_node);
3564 return err;
3565}
3566
3567static void mlxsw_sp_fib_node_fini(struct mlxsw_sp *mlxsw_sp,
3568 struct mlxsw_sp_fib_node *fib_node)
3569{
76610ebb
IS
3570 struct mlxsw_sp_fib *fib = fib_node->fib;
3571
3572 mlxsw_sp_fib_node_prefix_dec(fib_node);
fc922bb0 3573 mlxsw_sp_fib_lpm_tree_unlink(mlxsw_sp, fib);
76610ebb
IS
3574 fib_node->fib = NULL;
3575 mlxsw_sp_fib_node_remove(fib, fib_node);
3576}
3577
9aecce1c 3578static struct mlxsw_sp_fib_node *
731ea1ca
IS
3579mlxsw_sp_fib_node_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id, const void *addr,
3580 size_t addr_len, unsigned char prefix_len,
3581 enum mlxsw_sp_l3proto proto)
5b004412 3582{
9aecce1c 3583 struct mlxsw_sp_fib_node *fib_node;
76610ebb 3584 struct mlxsw_sp_fib *fib;
9aecce1c
IS
3585 struct mlxsw_sp_vr *vr;
3586 int err;
3587
731ea1ca 3588 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id);
9aecce1c
IS
3589 if (IS_ERR(vr))
3590 return ERR_CAST(vr);
731ea1ca 3591 fib = mlxsw_sp_vr_fib(vr, proto);
9aecce1c 3592
731ea1ca 3593 fib_node = mlxsw_sp_fib_node_lookup(fib, addr, addr_len, prefix_len);
9aecce1c
IS
3594 if (fib_node)
3595 return fib_node;
5b004412 3596
731ea1ca 3597 fib_node = mlxsw_sp_fib_node_create(fib, addr, addr_len, prefix_len);
9aecce1c
IS
3598 if (!fib_node) {
3599 err = -ENOMEM;
3600 goto err_fib_node_create;
5b004412 3601 }
9aecce1c 3602
76610ebb
IS
3603 err = mlxsw_sp_fib_node_init(mlxsw_sp, fib_node, fib);
3604 if (err)
3605 goto err_fib_node_init;
3606
9aecce1c
IS
3607 return fib_node;
3608
76610ebb
IS
3609err_fib_node_init:
3610 mlxsw_sp_fib_node_destroy(fib_node);
9aecce1c 3611err_fib_node_create:
76610ebb 3612 mlxsw_sp_vr_put(vr);
9aecce1c 3613 return ERR_PTR(err);
5b004412
JP
3614}
3615
731ea1ca
IS
3616static void mlxsw_sp_fib_node_put(struct mlxsw_sp *mlxsw_sp,
3617 struct mlxsw_sp_fib_node *fib_node)
5b004412 3618{
76610ebb 3619 struct mlxsw_sp_vr *vr = fib_node->fib->vr;
5b004412 3620
9aecce1c
IS
3621 if (!list_empty(&fib_node->entry_list))
3622 return;
76610ebb 3623 mlxsw_sp_fib_node_fini(mlxsw_sp, fib_node);
9aecce1c 3624 mlxsw_sp_fib_node_destroy(fib_node);
76610ebb 3625 mlxsw_sp_vr_put(vr);
61c503f9
JP
3626}
3627
4f1c7f1f 3628static struct mlxsw_sp_fib4_entry *
9aecce1c 3629mlxsw_sp_fib4_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
4f1c7f1f 3630 const struct mlxsw_sp_fib4_entry *new4_entry)
61c503f9 3631{
4f1c7f1f 3632 struct mlxsw_sp_fib4_entry *fib4_entry;
9aecce1c 3633
4f1c7f1f
IS
3634 list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) {
3635 if (fib4_entry->tb_id > new4_entry->tb_id)
9aecce1c 3636 continue;
4f1c7f1f 3637 if (fib4_entry->tb_id != new4_entry->tb_id)
9aecce1c 3638 break;
4f1c7f1f 3639 if (fib4_entry->tos > new4_entry->tos)
9aecce1c 3640 continue;
4f1c7f1f
IS
3641 if (fib4_entry->prio >= new4_entry->prio ||
3642 fib4_entry->tos < new4_entry->tos)
3643 return fib4_entry;
9aecce1c
IS
3644 }
3645
3646 return NULL;
3647}
3648
4f1c7f1f
IS
3649static int
3650mlxsw_sp_fib4_node_list_append(struct mlxsw_sp_fib4_entry *fib4_entry,
3651 struct mlxsw_sp_fib4_entry *new4_entry)
4283bce5
IS
3652{
3653 struct mlxsw_sp_fib_node *fib_node;
3654
4f1c7f1f 3655 if (WARN_ON(!fib4_entry))
4283bce5
IS
3656 return -EINVAL;
3657
4f1c7f1f
IS
3658 fib_node = fib4_entry->common.fib_node;
3659 list_for_each_entry_from(fib4_entry, &fib_node->entry_list,
3660 common.list) {
3661 if (fib4_entry->tb_id != new4_entry->tb_id ||
3662 fib4_entry->tos != new4_entry->tos ||
3663 fib4_entry->prio != new4_entry->prio)
4283bce5
IS
3664 break;
3665 }
3666
4f1c7f1f 3667 list_add_tail(&new4_entry->common.list, &fib4_entry->common.list);
4283bce5
IS
3668 return 0;
3669}
3670
9aecce1c 3671static int
9efbee6f 3672mlxsw_sp_fib4_node_list_insert(struct mlxsw_sp_fib4_entry *new4_entry,
599cf8f9 3673 bool replace, bool append)
9aecce1c 3674{
9efbee6f 3675 struct mlxsw_sp_fib_node *fib_node = new4_entry->common.fib_node;
4f1c7f1f 3676 struct mlxsw_sp_fib4_entry *fib4_entry;
9aecce1c 3677
4f1c7f1f 3678 fib4_entry = mlxsw_sp_fib4_node_entry_find(fib_node, new4_entry);
9aecce1c 3679
4283bce5 3680 if (append)
4f1c7f1f
IS
3681 return mlxsw_sp_fib4_node_list_append(fib4_entry, new4_entry);
3682 if (replace && WARN_ON(!fib4_entry))
599cf8f9 3683 return -EINVAL;
4283bce5 3684
599cf8f9
IS
3685 /* Insert new entry before replaced one, so that we can later
3686 * remove the second.
3687 */
4f1c7f1f
IS
3688 if (fib4_entry) {
3689 list_add_tail(&new4_entry->common.list,
3690 &fib4_entry->common.list);
9aecce1c 3691 } else {
4f1c7f1f 3692 struct mlxsw_sp_fib4_entry *last;
9aecce1c 3693
4f1c7f1f
IS
3694 list_for_each_entry(last, &fib_node->entry_list, common.list) {
3695 if (new4_entry->tb_id > last->tb_id)
9aecce1c 3696 break;
4f1c7f1f 3697 fib4_entry = last;
9aecce1c
IS
3698 }
3699
4f1c7f1f
IS
3700 if (fib4_entry)
3701 list_add(&new4_entry->common.list,
3702 &fib4_entry->common.list);
9aecce1c 3703 else
4f1c7f1f
IS
3704 list_add(&new4_entry->common.list,
3705 &fib_node->entry_list);
9aecce1c
IS
3706 }
3707
3708 return 0;
3709}
3710
3711static void
4f1c7f1f 3712mlxsw_sp_fib4_node_list_remove(struct mlxsw_sp_fib4_entry *fib4_entry)
9aecce1c 3713{
4f1c7f1f 3714 list_del(&fib4_entry->common.list);
9aecce1c
IS
3715}
3716
80c238f9
IS
3717static int mlxsw_sp_fib_node_entry_add(struct mlxsw_sp *mlxsw_sp,
3718 struct mlxsw_sp_fib_entry *fib_entry)
9aecce1c 3719{
9efbee6f
IS
3720 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
3721
9aecce1c
IS
3722 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
3723 return 0;
3724
3725 /* To prevent packet loss, overwrite the previously offloaded
3726 * entry.
3727 */
3728 if (!list_is_singular(&fib_node->entry_list)) {
3729 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
3730 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
3731
3732 mlxsw_sp_fib_entry_offload_refresh(n, op, 0);
3733 }
3734
3735 return mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
3736}
3737
80c238f9
IS
3738static void mlxsw_sp_fib_node_entry_del(struct mlxsw_sp *mlxsw_sp,
3739 struct mlxsw_sp_fib_entry *fib_entry)
9aecce1c 3740{
9efbee6f
IS
3741 struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
3742
9aecce1c
IS
3743 if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry))
3744 return;
3745
3746 /* Promote the next entry by overwriting the deleted entry */
3747 if (!list_is_singular(&fib_node->entry_list)) {
3748 struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list);
3749 enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
3750
3751 mlxsw_sp_fib_entry_update(mlxsw_sp, n);
3752 mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0);
3753 return;
3754 }
3755
3756 mlxsw_sp_fib_entry_del(mlxsw_sp, fib_entry);
3757}
3758
3759static int mlxsw_sp_fib4_node_entry_link(struct mlxsw_sp *mlxsw_sp,
4f1c7f1f 3760 struct mlxsw_sp_fib4_entry *fib4_entry,
599cf8f9 3761 bool replace, bool append)
9aecce1c 3762{
9aecce1c
IS
3763 int err;
3764
9efbee6f 3765 err = mlxsw_sp_fib4_node_list_insert(fib4_entry, replace, append);
9aecce1c
IS
3766 if (err)
3767 return err;
3768
80c238f9 3769 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib4_entry->common);
9aecce1c 3770 if (err)
80c238f9 3771 goto err_fib_node_entry_add;
9aecce1c 3772
9aecce1c
IS
3773 return 0;
3774
80c238f9 3775err_fib_node_entry_add:
4f1c7f1f 3776 mlxsw_sp_fib4_node_list_remove(fib4_entry);
9aecce1c
IS
3777 return err;
3778}
3779
3780static void
3781mlxsw_sp_fib4_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
4f1c7f1f 3782 struct mlxsw_sp_fib4_entry *fib4_entry)
9aecce1c 3783{
80c238f9 3784 mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib4_entry->common);
4f1c7f1f 3785 mlxsw_sp_fib4_node_list_remove(fib4_entry);
4607f6d2
PM
3786
3787 if (fib4_entry->common.type == MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP)
3788 mlxsw_sp_fib_entry_decap_fini(mlxsw_sp, &fib4_entry->common);
9aecce1c
IS
3789}
3790
599cf8f9 3791static void mlxsw_sp_fib4_entry_replace(struct mlxsw_sp *mlxsw_sp,
4f1c7f1f 3792 struct mlxsw_sp_fib4_entry *fib4_entry,
599cf8f9
IS
3793 bool replace)
3794{
4f1c7f1f
IS
3795 struct mlxsw_sp_fib_node *fib_node = fib4_entry->common.fib_node;
3796 struct mlxsw_sp_fib4_entry *replaced;
599cf8f9
IS
3797
3798 if (!replace)
3799 return;
3800
3801 /* We inserted the new entry before replaced one */
4f1c7f1f 3802 replaced = list_next_entry(fib4_entry, common.list);
599cf8f9
IS
3803
3804 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, replaced);
3805 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, replaced);
731ea1ca 3806 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
599cf8f9
IS
3807}
3808
9aecce1c
IS
3809static int
3810mlxsw_sp_router_fib4_add(struct mlxsw_sp *mlxsw_sp,
4283bce5 3811 const struct fib_entry_notifier_info *fen_info,
599cf8f9 3812 bool replace, bool append)
9aecce1c 3813{
4f1c7f1f 3814 struct mlxsw_sp_fib4_entry *fib4_entry;
9aecce1c 3815 struct mlxsw_sp_fib_node *fib_node;
61c503f9
JP
3816 int err;
3817
9011b677 3818 if (mlxsw_sp->router->aborted)
b45f64d1
JP
3819 return 0;
3820
731ea1ca
IS
3821 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, fen_info->tb_id,
3822 &fen_info->dst, sizeof(fen_info->dst),
3823 fen_info->dst_len,
3824 MLXSW_SP_L3_PROTO_IPV4);
9aecce1c
IS
3825 if (IS_ERR(fib_node)) {
3826 dev_warn(mlxsw_sp->bus_info->dev, "Failed to get FIB node\n");
3827 return PTR_ERR(fib_node);
b45f64d1 3828 }
61c503f9 3829
4f1c7f1f
IS
3830 fib4_entry = mlxsw_sp_fib4_entry_create(mlxsw_sp, fib_node, fen_info);
3831 if (IS_ERR(fib4_entry)) {
9aecce1c 3832 dev_warn(mlxsw_sp->bus_info->dev, "Failed to create FIB entry\n");
4f1c7f1f 3833 err = PTR_ERR(fib4_entry);
9aecce1c
IS
3834 goto err_fib4_entry_create;
3835 }
5b004412 3836
4f1c7f1f 3837 err = mlxsw_sp_fib4_node_entry_link(mlxsw_sp, fib4_entry, replace,
599cf8f9 3838 append);
b45f64d1 3839 if (err) {
9aecce1c
IS
3840 dev_warn(mlxsw_sp->bus_info->dev, "Failed to link FIB entry to node\n");
3841 goto err_fib4_node_entry_link;
b45f64d1 3842 }
9aecce1c 3843
4f1c7f1f 3844 mlxsw_sp_fib4_entry_replace(mlxsw_sp, fib4_entry, replace);
599cf8f9 3845
61c503f9
JP
3846 return 0;
3847
9aecce1c 3848err_fib4_node_entry_link:
4f1c7f1f 3849 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
9aecce1c 3850err_fib4_entry_create:
731ea1ca 3851 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
61c503f9
JP
3852 return err;
3853}
3854
37956d78
JP
3855static void mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp,
3856 struct fib_entry_notifier_info *fen_info)
61c503f9 3857{
4f1c7f1f 3858 struct mlxsw_sp_fib4_entry *fib4_entry;
9aecce1c 3859 struct mlxsw_sp_fib_node *fib_node;
61c503f9 3860
9011b677 3861 if (mlxsw_sp->router->aborted)
37956d78 3862 return;
b45f64d1 3863
4f1c7f1f
IS
3864 fib4_entry = mlxsw_sp_fib4_entry_lookup(mlxsw_sp, fen_info);
3865 if (WARN_ON(!fib4_entry))
37956d78 3866 return;
4f1c7f1f 3867 fib_node = fib4_entry->common.fib_node;
5b004412 3868
4f1c7f1f
IS
3869 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
3870 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
731ea1ca 3871 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
61c503f9 3872}
b45f64d1 3873
428b851f
IS
3874static bool mlxsw_sp_fib6_rt_should_ignore(const struct rt6_info *rt)
3875{
3876 /* Packets with link-local destination IP arriving to the router
3877 * are trapped to the CPU, so no need to program specific routes
3878 * for them.
3879 */
3880 if (ipv6_addr_type(&rt->rt6i_dst.addr) & IPV6_ADDR_LINKLOCAL)
3881 return true;
3882
3883 /* Multicast routes aren't supported, so ignore them. Neighbour
3884 * Discovery packets are specifically trapped.
3885 */
3886 if (ipv6_addr_type(&rt->rt6i_dst.addr) & IPV6_ADDR_MULTICAST)
3887 return true;
3888
3889 /* Cloned routes are irrelevant in the forwarding path. */
3890 if (rt->rt6i_flags & RTF_CACHE)
3891 return true;
3892
3893 return false;
3894}
3895
3896static struct mlxsw_sp_rt6 *mlxsw_sp_rt6_create(struct rt6_info *rt)
3897{
3898 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3899
3900 mlxsw_sp_rt6 = kzalloc(sizeof(*mlxsw_sp_rt6), GFP_KERNEL);
3901 if (!mlxsw_sp_rt6)
3902 return ERR_PTR(-ENOMEM);
3903
3904 /* In case of route replace, replaced route is deleted with
3905 * no notification. Take reference to prevent accessing freed
3906 * memory.
3907 */
3908 mlxsw_sp_rt6->rt = rt;
3909 rt6_hold(rt);
3910
3911 return mlxsw_sp_rt6;
3912}
3913
3914#if IS_ENABLED(CONFIG_IPV6)
3915static void mlxsw_sp_rt6_release(struct rt6_info *rt)
3916{
3917 rt6_release(rt);
3918}
3919#else
3920static void mlxsw_sp_rt6_release(struct rt6_info *rt)
3921{
3922}
3923#endif
3924
3925static void mlxsw_sp_rt6_destroy(struct mlxsw_sp_rt6 *mlxsw_sp_rt6)
3926{
3927 mlxsw_sp_rt6_release(mlxsw_sp_rt6->rt);
3928 kfree(mlxsw_sp_rt6);
3929}
3930
3931static bool mlxsw_sp_fib6_rt_can_mp(const struct rt6_info *rt)
3932{
3933 /* RTF_CACHE routes are ignored */
3934 return (rt->rt6i_flags & (RTF_GATEWAY | RTF_ADDRCONF)) == RTF_GATEWAY;
3935}
3936
3937static struct rt6_info *
3938mlxsw_sp_fib6_entry_rt(const struct mlxsw_sp_fib6_entry *fib6_entry)
3939{
3940 return list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,
3941 list)->rt;
3942}
3943
3944static struct mlxsw_sp_fib6_entry *
3945mlxsw_sp_fib6_node_mp_entry_find(const struct mlxsw_sp_fib_node *fib_node,
0a7fd1ac 3946 const struct rt6_info *nrt, bool replace)
428b851f
IS
3947{
3948 struct mlxsw_sp_fib6_entry *fib6_entry;
3949
0a7fd1ac 3950 if (!mlxsw_sp_fib6_rt_can_mp(nrt) || replace)
428b851f
IS
3951 return NULL;
3952
3953 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
3954 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
3955
3956 /* RT6_TABLE_LOCAL and RT6_TABLE_MAIN share the same
3957 * virtual router.
3958 */
3959 if (rt->rt6i_table->tb6_id > nrt->rt6i_table->tb6_id)
3960 continue;
3961 if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id)
3962 break;
3963 if (rt->rt6i_metric < nrt->rt6i_metric)
3964 continue;
3965 if (rt->rt6i_metric == nrt->rt6i_metric &&
3966 mlxsw_sp_fib6_rt_can_mp(rt))
3967 return fib6_entry;
3968 if (rt->rt6i_metric > nrt->rt6i_metric)
3969 break;
3970 }
3971
3972 return NULL;
3973}
3974
3975static struct mlxsw_sp_rt6 *
3976mlxsw_sp_fib6_entry_rt_find(const struct mlxsw_sp_fib6_entry *fib6_entry,
3977 const struct rt6_info *rt)
3978{
3979 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
3980
3981 list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
3982 if (mlxsw_sp_rt6->rt == rt)
3983 return mlxsw_sp_rt6;
3984 }
3985
3986 return NULL;
3987}
3988
8f28a309
PM
3989static bool mlxsw_sp_nexthop6_ipip_type(const struct mlxsw_sp *mlxsw_sp,
3990 const struct rt6_info *rt,
3991 enum mlxsw_sp_ipip_type *ret)
3992{
3993 return rt->dst.dev &&
3994 mlxsw_sp_netdev_ipip_type(mlxsw_sp, rt->dst.dev, ret);
3995}
3996
35225e47
PM
3997static int mlxsw_sp_nexthop6_type_init(struct mlxsw_sp *mlxsw_sp,
3998 struct mlxsw_sp_nexthop_group *nh_grp,
3999 struct mlxsw_sp_nexthop *nh,
4000 const struct rt6_info *rt)
428b851f 4001{
8f28a309 4002 struct mlxsw_sp_router *router = mlxsw_sp->router;
428b851f 4003 struct net_device *dev = rt->dst.dev;
8f28a309 4004 enum mlxsw_sp_ipip_type ipipt;
428b851f
IS
4005 struct mlxsw_sp_rif *rif;
4006 int err;
4007
8f28a309
PM
4008 if (mlxsw_sp_nexthop6_ipip_type(mlxsw_sp, rt, &ipipt) &&
4009 router->ipip_ops_arr[ipipt]->can_offload(mlxsw_sp, dev,
4010 MLXSW_SP_L3_PROTO_IPV6)) {
4011 nh->type = MLXSW_SP_NEXTHOP_TYPE_IPIP;
4012 return mlxsw_sp_nexthop_ipip_init(mlxsw_sp, ipipt, nh, dev);
4013 }
4014
35225e47 4015 nh->type = MLXSW_SP_NEXTHOP_TYPE_ETH;
428b851f
IS
4016 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
4017 if (!rif)
4018 return 0;
4019 mlxsw_sp_nexthop_rif_init(nh, rif);
4020
4021 err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
4022 if (err)
4023 goto err_nexthop_neigh_init;
4024
4025 return 0;
4026
4027err_nexthop_neigh_init:
4028 mlxsw_sp_nexthop_rif_fini(nh);
4029 return err;
4030}
4031
35225e47
PM
4032static void mlxsw_sp_nexthop6_type_fini(struct mlxsw_sp *mlxsw_sp,
4033 struct mlxsw_sp_nexthop *nh)
4034{
4035 mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);
4036}
4037
4038static int mlxsw_sp_nexthop6_init(struct mlxsw_sp *mlxsw_sp,
4039 struct mlxsw_sp_nexthop_group *nh_grp,
4040 struct mlxsw_sp_nexthop *nh,
4041 const struct rt6_info *rt)
4042{
4043 struct net_device *dev = rt->dst.dev;
4044
4045 nh->nh_grp = nh_grp;
4046 memcpy(&nh->gw_addr, &rt->rt6i_gateway, sizeof(nh->gw_addr));
4047
4048 if (!dev)
4049 return 0;
4050 nh->ifindex = dev->ifindex;
4051
4052 return mlxsw_sp_nexthop6_type_init(mlxsw_sp, nh_grp, nh, rt);
4053}
4054
428b851f
IS
4055static void mlxsw_sp_nexthop6_fini(struct mlxsw_sp *mlxsw_sp,
4056 struct mlxsw_sp_nexthop *nh)
4057{
35225e47 4058 mlxsw_sp_nexthop6_type_fini(mlxsw_sp, nh);
428b851f
IS
4059}
4060
f6050ee6
PM
4061static bool mlxsw_sp_rt6_is_gateway(const struct mlxsw_sp *mlxsw_sp,
4062 const struct rt6_info *rt)
4063{
8f28a309
PM
4064 return rt->rt6i_flags & RTF_GATEWAY ||
4065 mlxsw_sp_nexthop6_ipip_type(mlxsw_sp, rt, NULL);
f6050ee6
PM
4066}
4067
428b851f
IS
4068static struct mlxsw_sp_nexthop_group *
4069mlxsw_sp_nexthop6_group_create(struct mlxsw_sp *mlxsw_sp,
4070 struct mlxsw_sp_fib6_entry *fib6_entry)
4071{
4072 struct mlxsw_sp_nexthop_group *nh_grp;
4073 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
4074 struct mlxsw_sp_nexthop *nh;
4075 size_t alloc_size;
4076 int i = 0;
4077 int err;
4078
4079 alloc_size = sizeof(*nh_grp) +
4080 fib6_entry->nrt6 * sizeof(struct mlxsw_sp_nexthop);
4081 nh_grp = kzalloc(alloc_size, GFP_KERNEL);
4082 if (!nh_grp)
4083 return ERR_PTR(-ENOMEM);
4084 INIT_LIST_HEAD(&nh_grp->fib_list);
4085#if IS_ENABLED(CONFIG_IPV6)
4086 nh_grp->neigh_tbl = &nd_tbl;
4087#endif
4088 mlxsw_sp_rt6 = list_first_entry(&fib6_entry->rt6_list,
4089 struct mlxsw_sp_rt6, list);
f6050ee6 4090 nh_grp->gateway = mlxsw_sp_rt6_is_gateway(mlxsw_sp, mlxsw_sp_rt6->rt);
428b851f
IS
4091 nh_grp->count = fib6_entry->nrt6;
4092 for (i = 0; i < nh_grp->count; i++) {
4093 struct rt6_info *rt = mlxsw_sp_rt6->rt;
4094
4095 nh = &nh_grp->nexthops[i];
4096 err = mlxsw_sp_nexthop6_init(mlxsw_sp, nh_grp, nh, rt);
4097 if (err)
4098 goto err_nexthop6_init;
4099 mlxsw_sp_rt6 = list_next_entry(mlxsw_sp_rt6, list);
4100 }
e6f3b379
AS
4101
4102 err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
4103 if (err)
4104 goto err_nexthop_group_insert;
4105
428b851f
IS
4106 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
4107 return nh_grp;
4108
e6f3b379 4109err_nexthop_group_insert:
428b851f
IS
4110err_nexthop6_init:
4111 for (i--; i >= 0; i--) {
4112 nh = &nh_grp->nexthops[i];
4113 mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
4114 }
4115 kfree(nh_grp);
4116 return ERR_PTR(err);
4117}
4118
4119static void
4120mlxsw_sp_nexthop6_group_destroy(struct mlxsw_sp *mlxsw_sp,
4121 struct mlxsw_sp_nexthop_group *nh_grp)
4122{
4123 struct mlxsw_sp_nexthop *nh;
4124 int i = nh_grp->count;
4125
e6f3b379 4126 mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
428b851f
IS
4127 for (i--; i >= 0; i--) {
4128 nh = &nh_grp->nexthops[i];
4129 mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
4130 }
4131 mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
4132 WARN_ON(nh_grp->adj_index_valid);
4133 kfree(nh_grp);
4134}
4135
4136static int mlxsw_sp_nexthop6_group_get(struct mlxsw_sp *mlxsw_sp,
4137 struct mlxsw_sp_fib6_entry *fib6_entry)
4138{
4139 struct mlxsw_sp_nexthop_group *nh_grp;
4140
e6f3b379
AS
4141 nh_grp = mlxsw_sp_nexthop6_group_lookup(mlxsw_sp, fib6_entry);
4142 if (!nh_grp) {
4143 nh_grp = mlxsw_sp_nexthop6_group_create(mlxsw_sp, fib6_entry);
4144 if (IS_ERR(nh_grp))
4145 return PTR_ERR(nh_grp);
4146 }
428b851f
IS
4147
4148 list_add_tail(&fib6_entry->common.nexthop_group_node,
4149 &nh_grp->fib_list);
4150 fib6_entry->common.nh_group = nh_grp;
4151
4152 return 0;
4153}
4154
4155static void mlxsw_sp_nexthop6_group_put(struct mlxsw_sp *mlxsw_sp,
4156 struct mlxsw_sp_fib_entry *fib_entry)
4157{
4158 struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group;
4159
4160 list_del(&fib_entry->nexthop_group_node);
4161 if (!list_empty(&nh_grp->fib_list))
4162 return;
4163 mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, nh_grp);
4164}
4165
4166static int
4167mlxsw_sp_nexthop6_group_update(struct mlxsw_sp *mlxsw_sp,
4168 struct mlxsw_sp_fib6_entry *fib6_entry)
4169{
4170 struct mlxsw_sp_nexthop_group *old_nh_grp = fib6_entry->common.nh_group;
4171 int err;
4172
4173 fib6_entry->common.nh_group = NULL;
4174 list_del(&fib6_entry->common.nexthop_group_node);
4175
4176 err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry);
4177 if (err)
4178 goto err_nexthop6_group_get;
4179
4180 /* In case this entry is offloaded, then the adjacency index
4181 * currently associated with it in the device's table is that
4182 * of the old group. Start using the new one instead.
4183 */
4184 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common);
4185 if (err)
4186 goto err_fib_node_entry_add;
4187
4188 if (list_empty(&old_nh_grp->fib_list))
4189 mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, old_nh_grp);
4190
4191 return 0;
4192
4193err_fib_node_entry_add:
4194 mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);
4195err_nexthop6_group_get:
4196 list_add_tail(&fib6_entry->common.nexthop_group_node,
4197 &old_nh_grp->fib_list);
4198 fib6_entry->common.nh_group = old_nh_grp;
4199 return err;
4200}
4201
4202static int
4203mlxsw_sp_fib6_entry_nexthop_add(struct mlxsw_sp *mlxsw_sp,
4204 struct mlxsw_sp_fib6_entry *fib6_entry,
4205 struct rt6_info *rt)
4206{
4207 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
4208 int err;
4209
4210 mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt);
4211 if (IS_ERR(mlxsw_sp_rt6))
4212 return PTR_ERR(mlxsw_sp_rt6);
4213
4214 list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list);
4215 fib6_entry->nrt6++;
4216
4217 err = mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);
4218 if (err)
4219 goto err_nexthop6_group_update;
4220
4221 return 0;
4222
4223err_nexthop6_group_update:
4224 fib6_entry->nrt6--;
4225 list_del(&mlxsw_sp_rt6->list);
4226 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
4227 return err;
4228}
4229
4230static void
4231mlxsw_sp_fib6_entry_nexthop_del(struct mlxsw_sp *mlxsw_sp,
4232 struct mlxsw_sp_fib6_entry *fib6_entry,
4233 struct rt6_info *rt)
4234{
4235 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
4236
4237 mlxsw_sp_rt6 = mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt);
4238 if (WARN_ON(!mlxsw_sp_rt6))
4239 return;
4240
4241 fib6_entry->nrt6--;
4242 list_del(&mlxsw_sp_rt6->list);
4243 mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);
4244 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
4245}
4246
f6050ee6
PM
4247static void mlxsw_sp_fib6_entry_type_set(struct mlxsw_sp *mlxsw_sp,
4248 struct mlxsw_sp_fib_entry *fib_entry,
428b851f
IS
4249 const struct rt6_info *rt)
4250{
4251 /* Packets hitting RTF_REJECT routes need to be discarded by the
4252 * stack. We can rely on their destination device not having a
4253 * RIF (it's the loopback device) and can thus use action type
4254 * local, which will cause them to be trapped with a lower
4255 * priority than packets that need to be locally received.
4256 */
d3b6d377 4257 if (rt->rt6i_flags & (RTF_LOCAL | RTF_ANYCAST))
428b851f
IS
4258 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
4259 else if (rt->rt6i_flags & RTF_REJECT)
4260 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
f6050ee6 4261 else if (mlxsw_sp_rt6_is_gateway(mlxsw_sp, rt))
428b851f
IS
4262 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
4263 else
4264 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
4265}
4266
4267static void
4268mlxsw_sp_fib6_entry_rt_destroy_all(struct mlxsw_sp_fib6_entry *fib6_entry)
4269{
4270 struct mlxsw_sp_rt6 *mlxsw_sp_rt6, *tmp;
4271
4272 list_for_each_entry_safe(mlxsw_sp_rt6, tmp, &fib6_entry->rt6_list,
4273 list) {
4274 fib6_entry->nrt6--;
4275 list_del(&mlxsw_sp_rt6->list);
4276 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
4277 }
4278}
4279
4280static struct mlxsw_sp_fib6_entry *
4281mlxsw_sp_fib6_entry_create(struct mlxsw_sp *mlxsw_sp,
4282 struct mlxsw_sp_fib_node *fib_node,
4283 struct rt6_info *rt)
4284{
4285 struct mlxsw_sp_fib6_entry *fib6_entry;
4286 struct mlxsw_sp_fib_entry *fib_entry;
4287 struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
4288 int err;
4289
4290 fib6_entry = kzalloc(sizeof(*fib6_entry), GFP_KERNEL);
4291 if (!fib6_entry)
4292 return ERR_PTR(-ENOMEM);
4293 fib_entry = &fib6_entry->common;
4294
4295 mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt);
4296 if (IS_ERR(mlxsw_sp_rt6)) {
4297 err = PTR_ERR(mlxsw_sp_rt6);
4298 goto err_rt6_create;
4299 }
4300
f6050ee6 4301 mlxsw_sp_fib6_entry_type_set(mlxsw_sp, fib_entry, mlxsw_sp_rt6->rt);
428b851f
IS
4302
4303 INIT_LIST_HEAD(&fib6_entry->rt6_list);
4304 list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list);
4305 fib6_entry->nrt6 = 1;
4306 err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry);
4307 if (err)
4308 goto err_nexthop6_group_get;
4309
4310 fib_entry->fib_node = fib_node;
4311
4312 return fib6_entry;
4313
4314err_nexthop6_group_get:
4315 list_del(&mlxsw_sp_rt6->list);
4316 mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
4317err_rt6_create:
4318 kfree(fib6_entry);
4319 return ERR_PTR(err);
4320}
4321
4322static void mlxsw_sp_fib6_entry_destroy(struct mlxsw_sp *mlxsw_sp,
4323 struct mlxsw_sp_fib6_entry *fib6_entry)
4324{
4325 mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);
4326 mlxsw_sp_fib6_entry_rt_destroy_all(fib6_entry);
4327 WARN_ON(fib6_entry->nrt6);
4328 kfree(fib6_entry);
4329}
4330
4331static struct mlxsw_sp_fib6_entry *
4332mlxsw_sp_fib6_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
0a7fd1ac 4333 const struct rt6_info *nrt, bool replace)
428b851f 4334{
0a7fd1ac 4335 struct mlxsw_sp_fib6_entry *fib6_entry, *fallback = NULL;
428b851f
IS
4336
4337 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
4338 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
4339
4340 if (rt->rt6i_table->tb6_id > nrt->rt6i_table->tb6_id)
4341 continue;
4342 if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id)
4343 break;
0a7fd1ac
IS
4344 if (replace && rt->rt6i_metric == nrt->rt6i_metric) {
4345 if (mlxsw_sp_fib6_rt_can_mp(rt) ==
4346 mlxsw_sp_fib6_rt_can_mp(nrt))
4347 return fib6_entry;
4348 if (mlxsw_sp_fib6_rt_can_mp(nrt))
4349 fallback = fallback ?: fib6_entry;
4350 }
428b851f 4351 if (rt->rt6i_metric > nrt->rt6i_metric)
0a7fd1ac 4352 return fallback ?: fib6_entry;
428b851f
IS
4353 }
4354
0a7fd1ac 4355 return fallback;
428b851f
IS
4356}
4357
4358static int
0a7fd1ac
IS
4359mlxsw_sp_fib6_node_list_insert(struct mlxsw_sp_fib6_entry *new6_entry,
4360 bool replace)
428b851f
IS
4361{
4362 struct mlxsw_sp_fib_node *fib_node = new6_entry->common.fib_node;
4363 struct rt6_info *nrt = mlxsw_sp_fib6_entry_rt(new6_entry);
4364 struct mlxsw_sp_fib6_entry *fib6_entry;
4365
0a7fd1ac
IS
4366 fib6_entry = mlxsw_sp_fib6_node_entry_find(fib_node, nrt, replace);
4367
4368 if (replace && WARN_ON(!fib6_entry))
4369 return -EINVAL;
428b851f
IS
4370
4371 if (fib6_entry) {
4372 list_add_tail(&new6_entry->common.list,
4373 &fib6_entry->common.list);
4374 } else {
4375 struct mlxsw_sp_fib6_entry *last;
4376
4377 list_for_each_entry(last, &fib_node->entry_list, common.list) {
4378 struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(last);
4379
4380 if (nrt->rt6i_table->tb6_id > rt->rt6i_table->tb6_id)
4381 break;
4382 fib6_entry = last;
4383 }
4384
4385 if (fib6_entry)
4386 list_add(&new6_entry->common.list,
4387 &fib6_entry->common.list);
4388 else
4389 list_add(&new6_entry->common.list,
4390 &fib_node->entry_list);
4391 }
4392
4393 return 0;
4394}
4395
4396static void
4397mlxsw_sp_fib6_node_list_remove(struct mlxsw_sp_fib6_entry *fib6_entry)
4398{
4399 list_del(&fib6_entry->common.list);
4400}
4401
4402static int mlxsw_sp_fib6_node_entry_link(struct mlxsw_sp *mlxsw_sp,
0a7fd1ac
IS
4403 struct mlxsw_sp_fib6_entry *fib6_entry,
4404 bool replace)
428b851f
IS
4405{
4406 int err;
4407
0a7fd1ac 4408 err = mlxsw_sp_fib6_node_list_insert(fib6_entry, replace);
428b851f
IS
4409 if (err)
4410 return err;
4411
4412 err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common);
4413 if (err)
4414 goto err_fib_node_entry_add;
4415
4416 return 0;
4417
4418err_fib_node_entry_add:
4419 mlxsw_sp_fib6_node_list_remove(fib6_entry);
4420 return err;
4421}
4422
4423static void
4424mlxsw_sp_fib6_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
4425 struct mlxsw_sp_fib6_entry *fib6_entry)
4426{
4427 mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib6_entry->common);
4428 mlxsw_sp_fib6_node_list_remove(fib6_entry);
4429}
4430
4431static struct mlxsw_sp_fib6_entry *
4432mlxsw_sp_fib6_entry_lookup(struct mlxsw_sp *mlxsw_sp,
4433 const struct rt6_info *rt)
4434{
4435 struct mlxsw_sp_fib6_entry *fib6_entry;
4436 struct mlxsw_sp_fib_node *fib_node;
4437 struct mlxsw_sp_fib *fib;
4438 struct mlxsw_sp_vr *vr;
4439
4440 vr = mlxsw_sp_vr_find(mlxsw_sp, rt->rt6i_table->tb6_id);
4441 if (!vr)
4442 return NULL;
4443 fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV6);
4444
4445 fib_node = mlxsw_sp_fib_node_lookup(fib, &rt->rt6i_dst.addr,
4446 sizeof(rt->rt6i_dst.addr),
4447 rt->rt6i_dst.plen);
4448 if (!fib_node)
4449 return NULL;
4450
4451 list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
4452 struct rt6_info *iter_rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
4453
4454 if (rt->rt6i_table->tb6_id == iter_rt->rt6i_table->tb6_id &&
4455 rt->rt6i_metric == iter_rt->rt6i_metric &&
4456 mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt))
4457 return fib6_entry;
4458 }
4459
4460 return NULL;
4461}
4462
0a7fd1ac
IS
4463static void mlxsw_sp_fib6_entry_replace(struct mlxsw_sp *mlxsw_sp,
4464 struct mlxsw_sp_fib6_entry *fib6_entry,
4465 bool replace)
4466{
4467 struct mlxsw_sp_fib_node *fib_node = fib6_entry->common.fib_node;
4468 struct mlxsw_sp_fib6_entry *replaced;
4469
4470 if (!replace)
4471 return;
4472
4473 replaced = list_next_entry(fib6_entry, common.list);
4474
4475 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, replaced);
4476 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, replaced);
4477 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
4478}
4479
428b851f 4480static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp,
0a7fd1ac 4481 struct rt6_info *rt, bool replace)
428b851f
IS
4482{
4483 struct mlxsw_sp_fib6_entry *fib6_entry;
4484 struct mlxsw_sp_fib_node *fib_node;
4485 int err;
4486
4487 if (mlxsw_sp->router->aborted)
4488 return 0;
4489
f36f5ac6
IS
4490 if (rt->rt6i_src.plen)
4491 return -EINVAL;
4492
428b851f
IS
4493 if (mlxsw_sp_fib6_rt_should_ignore(rt))
4494 return 0;
4495
4496 fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, rt->rt6i_table->tb6_id,
4497 &rt->rt6i_dst.addr,
4498 sizeof(rt->rt6i_dst.addr),
4499 rt->rt6i_dst.plen,
4500 MLXSW_SP_L3_PROTO_IPV6);
4501 if (IS_ERR(fib_node))
4502 return PTR_ERR(fib_node);
4503
4504 /* Before creating a new entry, try to append route to an existing
4505 * multipath entry.
4506 */
0a7fd1ac 4507 fib6_entry = mlxsw_sp_fib6_node_mp_entry_find(fib_node, rt, replace);
428b851f
IS
4508 if (fib6_entry) {
4509 err = mlxsw_sp_fib6_entry_nexthop_add(mlxsw_sp, fib6_entry, rt);
4510 if (err)
4511 goto err_fib6_entry_nexthop_add;
4512 return 0;
4513 }
4514
4515 fib6_entry = mlxsw_sp_fib6_entry_create(mlxsw_sp, fib_node, rt);
4516 if (IS_ERR(fib6_entry)) {
4517 err = PTR_ERR(fib6_entry);
4518 goto err_fib6_entry_create;
4519 }
4520
0a7fd1ac 4521 err = mlxsw_sp_fib6_node_entry_link(mlxsw_sp, fib6_entry, replace);
428b851f
IS
4522 if (err)
4523 goto err_fib6_node_entry_link;
4524
0a7fd1ac
IS
4525 mlxsw_sp_fib6_entry_replace(mlxsw_sp, fib6_entry, replace);
4526
428b851f
IS
4527 return 0;
4528
4529err_fib6_node_entry_link:
4530 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
4531err_fib6_entry_create:
4532err_fib6_entry_nexthop_add:
4533 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
4534 return err;
4535}
4536
4537static void mlxsw_sp_router_fib6_del(struct mlxsw_sp *mlxsw_sp,
4538 struct rt6_info *rt)
4539{
4540 struct mlxsw_sp_fib6_entry *fib6_entry;
4541 struct mlxsw_sp_fib_node *fib_node;
4542
4543 if (mlxsw_sp->router->aborted)
4544 return;
4545
4546 if (mlxsw_sp_fib6_rt_should_ignore(rt))
4547 return;
4548
4549 fib6_entry = mlxsw_sp_fib6_entry_lookup(mlxsw_sp, rt);
4550 if (WARN_ON(!fib6_entry))
4551 return;
4552
4553 /* If route is part of a multipath entry, but not the last one
4554 * removed, then only reduce its nexthop group.
4555 */
4556 if (!list_is_singular(&fib6_entry->rt6_list)) {
4557 mlxsw_sp_fib6_entry_nexthop_del(mlxsw_sp, fib6_entry, rt);
4558 return;
4559 }
4560
4561 fib_node = fib6_entry->common.fib_node;
4562
4563 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry);
4564 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
4565 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
4566}
4567
bc65a8a4
IS
4568static int __mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp,
4569 enum mlxsw_reg_ralxx_protocol proto,
4570 u8 tree_id)
b45f64d1
JP
4571{
4572 char ralta_pl[MLXSW_REG_RALTA_LEN];
4573 char ralst_pl[MLXSW_REG_RALST_LEN];
b5d90e6d 4574 int i, err;
b45f64d1 4575
bc65a8a4 4576 mlxsw_reg_ralta_pack(ralta_pl, true, proto, tree_id);
b45f64d1
JP
4577 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
4578 if (err)
4579 return err;
4580
bc65a8a4 4581 mlxsw_reg_ralst_pack(ralst_pl, 0xff, tree_id);
b45f64d1
JP
4582 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
4583 if (err)
4584 return err;
4585
b5d90e6d 4586 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
9011b677 4587 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
b5d90e6d
IS
4588 char raltb_pl[MLXSW_REG_RALTB_LEN];
4589 char ralue_pl[MLXSW_REG_RALUE_LEN];
b45f64d1 4590
bc65a8a4 4591 mlxsw_reg_raltb_pack(raltb_pl, vr->id, proto, tree_id);
b5d90e6d
IS
4592 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb),
4593 raltb_pl);
4594 if (err)
4595 return err;
4596
bc65a8a4
IS
4597 mlxsw_reg_ralue_pack(ralue_pl, proto,
4598 MLXSW_REG_RALUE_OP_WRITE_WRITE, vr->id, 0);
b5d90e6d
IS
4599 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
4600 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue),
4601 ralue_pl);
4602 if (err)
4603 return err;
4604 }
4605
4606 return 0;
b45f64d1
JP
4607}
4608
bc65a8a4
IS
4609static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp)
4610{
4611 enum mlxsw_reg_ralxx_protocol proto = MLXSW_REG_RALXX_PROTOCOL_IPV4;
4612 int err;
4613
4614 err = __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
4615 MLXSW_SP_LPM_TREE_MIN);
4616 if (err)
4617 return err;
4618
4619 proto = MLXSW_REG_RALXX_PROTOCOL_IPV6;
4620 return __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
4621 MLXSW_SP_LPM_TREE_MIN + 1);
4622}
4623
9aecce1c
IS
4624static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp,
4625 struct mlxsw_sp_fib_node *fib_node)
4626{
4f1c7f1f 4627 struct mlxsw_sp_fib4_entry *fib4_entry, *tmp;
9aecce1c 4628
4f1c7f1f
IS
4629 list_for_each_entry_safe(fib4_entry, tmp, &fib_node->entry_list,
4630 common.list) {
4631 bool do_break = &tmp->common.list == &fib_node->entry_list;
9aecce1c 4632
4f1c7f1f
IS
4633 mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry);
4634 mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
731ea1ca 4635 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
9aecce1c
IS
4636 /* Break when entry list is empty and node was freed.
4637 * Otherwise, we'll access freed memory in the next
4638 * iteration.
4639 */
4640 if (do_break)
4641 break;
4642 }
4643}
4644
428b851f
IS
4645static void mlxsw_sp_fib6_node_flush(struct mlxsw_sp *mlxsw_sp,
4646 struct mlxsw_sp_fib_node *fib_node)
4647{
4648 struct mlxsw_sp_fib6_entry *fib6_entry, *tmp;
4649
4650 list_for_each_entry_safe(fib6_entry, tmp, &fib_node->entry_list,
4651 common.list) {
4652 bool do_break = &tmp->common.list == &fib_node->entry_list;
4653
4654 mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry);
4655 mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
4656 mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
4657 if (do_break)
4658 break;
4659 }
4660}
4661
9aecce1c
IS
4662static void mlxsw_sp_fib_node_flush(struct mlxsw_sp *mlxsw_sp,
4663 struct mlxsw_sp_fib_node *fib_node)
4664{
76610ebb 4665 switch (fib_node->fib->proto) {
9aecce1c
IS
4666 case MLXSW_SP_L3_PROTO_IPV4:
4667 mlxsw_sp_fib4_node_flush(mlxsw_sp, fib_node);
4668 break;
4669 case MLXSW_SP_L3_PROTO_IPV6:
428b851f 4670 mlxsw_sp_fib6_node_flush(mlxsw_sp, fib_node);
9aecce1c
IS
4671 break;
4672 }
4673}
4674
76610ebb
IS
4675static void mlxsw_sp_vr_fib_flush(struct mlxsw_sp *mlxsw_sp,
4676 struct mlxsw_sp_vr *vr,
4677 enum mlxsw_sp_l3proto proto)
b45f64d1 4678{
76610ebb 4679 struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
9aecce1c 4680 struct mlxsw_sp_fib_node *fib_node, *tmp;
76610ebb
IS
4681
4682 list_for_each_entry_safe(fib_node, tmp, &fib->node_list, list) {
4683 bool do_break = &tmp->list == &fib->node_list;
4684
4685 mlxsw_sp_fib_node_flush(mlxsw_sp, fib_node);
4686 if (do_break)
4687 break;
4688 }
4689}
4690
4691static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp)
4692{
b45f64d1 4693 int i;
b45f64d1 4694
c1a38311 4695 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
9011b677 4696 struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
ac571de9 4697
76610ebb 4698 if (!mlxsw_sp_vr_is_used(vr))
b45f64d1 4699 continue;
76610ebb 4700 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV4);
a3d9bc50
IS
4701
4702 /* If virtual router was only used for IPv4, then it's no
4703 * longer used.
4704 */
4705 if (!mlxsw_sp_vr_is_used(vr))
4706 continue;
4707 mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV6);
b45f64d1 4708 }
ac571de9
IS
4709}
4710
bc65a8a4 4711static void mlxsw_sp_router_fib_abort(struct mlxsw_sp *mlxsw_sp)
ac571de9
IS
4712{
4713 int err;
4714
9011b677 4715 if (mlxsw_sp->router->aborted)
d331d303
IS
4716 return;
4717 dev_warn(mlxsw_sp->bus_info->dev, "FIB abort triggered. Note that FIB entries are no longer being offloaded to this device.\n");
ac571de9 4718 mlxsw_sp_router_fib_flush(mlxsw_sp);
9011b677 4719 mlxsw_sp->router->aborted = true;
b45f64d1
JP
4720 err = mlxsw_sp_router_set_abort_trap(mlxsw_sp);
4721 if (err)
4722 dev_warn(mlxsw_sp->bus_info->dev, "Failed to set abort trap.\n");
4723}
4724
3057224e 4725struct mlxsw_sp_fib_event_work {
a0e4761d 4726 struct work_struct work;
ad178c8e 4727 union {
428b851f 4728 struct fib6_entry_notifier_info fen6_info;
ad178c8e 4729 struct fib_entry_notifier_info fen_info;
5d7bfd14 4730 struct fib_rule_notifier_info fr_info;
ad178c8e
IS
4731 struct fib_nh_notifier_info fnh_info;
4732 };
3057224e
IS
4733 struct mlxsw_sp *mlxsw_sp;
4734 unsigned long event;
4735};
4736
66a5763a 4737static void mlxsw_sp_router_fib4_event_work(struct work_struct *work)
b45f64d1 4738{
3057224e 4739 struct mlxsw_sp_fib_event_work *fib_work =
a0e4761d 4740 container_of(work, struct mlxsw_sp_fib_event_work, work);
3057224e 4741 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
5d7bfd14 4742 struct fib_rule *rule;
599cf8f9 4743 bool replace, append;
b45f64d1
JP
4744 int err;
4745
3057224e
IS
4746 /* Protect internal structures from changes */
4747 rtnl_lock();
4748 switch (fib_work->event) {
599cf8f9 4749 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
4283bce5 4750 case FIB_EVENT_ENTRY_APPEND: /* fall through */
b45f64d1 4751 case FIB_EVENT_ENTRY_ADD:
599cf8f9 4752 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
4283bce5
IS
4753 append = fib_work->event == FIB_EVENT_ENTRY_APPEND;
4754 err = mlxsw_sp_router_fib4_add(mlxsw_sp, &fib_work->fen_info,
599cf8f9 4755 replace, append);
b45f64d1 4756 if (err)
bc65a8a4 4757 mlxsw_sp_router_fib_abort(mlxsw_sp);
3057224e 4758 fib_info_put(fib_work->fen_info.fi);
b45f64d1
JP
4759 break;
4760 case FIB_EVENT_ENTRY_DEL:
3057224e
IS
4761 mlxsw_sp_router_fib4_del(mlxsw_sp, &fib_work->fen_info);
4762 fib_info_put(fib_work->fen_info.fi);
b45f64d1
JP
4763 break;
4764 case FIB_EVENT_RULE_ADD: /* fall through */
4765 case FIB_EVENT_RULE_DEL:
5d7bfd14 4766 rule = fib_work->fr_info.rule;
c7f6e665 4767 if (!fib4_rule_default(rule) && !rule->l3mdev)
bc65a8a4 4768 mlxsw_sp_router_fib_abort(mlxsw_sp);
5d7bfd14 4769 fib_rule_put(rule);
b45f64d1 4770 break;
ad178c8e
IS
4771 case FIB_EVENT_NH_ADD: /* fall through */
4772 case FIB_EVENT_NH_DEL:
0e6ea2a4
IS
4773 mlxsw_sp_nexthop4_event(mlxsw_sp, fib_work->event,
4774 fib_work->fnh_info.fib_nh);
ad178c8e
IS
4775 fib_info_put(fib_work->fnh_info.fib_nh->nh_parent);
4776 break;
b45f64d1 4777 }
3057224e
IS
4778 rtnl_unlock();
4779 kfree(fib_work);
4780}
4781
66a5763a
IS
4782static void mlxsw_sp_router_fib6_event_work(struct work_struct *work)
4783{
583419fd
IS
4784 struct mlxsw_sp_fib_event_work *fib_work =
4785 container_of(work, struct mlxsw_sp_fib_event_work, work);
4786 struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
4787 struct fib_rule *rule;
0a7fd1ac 4788 bool replace;
428b851f 4789 int err;
583419fd
IS
4790
4791 rtnl_lock();
4792 switch (fib_work->event) {
0a7fd1ac 4793 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
428b851f 4794 case FIB_EVENT_ENTRY_ADD:
0a7fd1ac 4795 replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
428b851f 4796 err = mlxsw_sp_router_fib6_add(mlxsw_sp,
0a7fd1ac 4797 fib_work->fen6_info.rt, replace);
428b851f
IS
4798 if (err)
4799 mlxsw_sp_router_fib_abort(mlxsw_sp);
4800 mlxsw_sp_rt6_release(fib_work->fen6_info.rt);
4801 break;
4802 case FIB_EVENT_ENTRY_DEL:
4803 mlxsw_sp_router_fib6_del(mlxsw_sp, fib_work->fen6_info.rt);
4804 mlxsw_sp_rt6_release(fib_work->fen6_info.rt);
4805 break;
583419fd
IS
4806 case FIB_EVENT_RULE_ADD: /* fall through */
4807 case FIB_EVENT_RULE_DEL:
4808 rule = fib_work->fr_info.rule;
4809 if (!fib6_rule_default(rule) && !rule->l3mdev)
4810 mlxsw_sp_router_fib_abort(mlxsw_sp);
4811 fib_rule_put(rule);
4812 break;
4813 }
4814 rtnl_unlock();
4815 kfree(fib_work);
66a5763a
IS
4816}
4817
4818static void mlxsw_sp_router_fib4_event(struct mlxsw_sp_fib_event_work *fib_work,
4819 struct fib_notifier_info *info)
4820{
4821 switch (fib_work->event) {
4822 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
4823 case FIB_EVENT_ENTRY_APPEND: /* fall through */
4824 case FIB_EVENT_ENTRY_ADD: /* fall through */
4825 case FIB_EVENT_ENTRY_DEL:
4826 memcpy(&fib_work->fen_info, info, sizeof(fib_work->fen_info));
4827 /* Take referece on fib_info to prevent it from being
4828 * freed while work is queued. Release it afterwards.
4829 */
4830 fib_info_hold(fib_work->fen_info.fi);
4831 break;
4832 case FIB_EVENT_RULE_ADD: /* fall through */
4833 case FIB_EVENT_RULE_DEL:
4834 memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info));
4835 fib_rule_get(fib_work->fr_info.rule);
4836 break;
4837 case FIB_EVENT_NH_ADD: /* fall through */
4838 case FIB_EVENT_NH_DEL:
4839 memcpy(&fib_work->fnh_info, info, sizeof(fib_work->fnh_info));
4840 fib_info_hold(fib_work->fnh_info.fib_nh->nh_parent);
4841 break;
4842 }
4843}
4844
4845static void mlxsw_sp_router_fib6_event(struct mlxsw_sp_fib_event_work *fib_work,
4846 struct fib_notifier_info *info)
4847{
583419fd 4848 switch (fib_work->event) {
0a7fd1ac 4849 case FIB_EVENT_ENTRY_REPLACE: /* fall through */
428b851f
IS
4850 case FIB_EVENT_ENTRY_ADD: /* fall through */
4851 case FIB_EVENT_ENTRY_DEL:
4852 memcpy(&fib_work->fen6_info, info, sizeof(fib_work->fen6_info));
4853 rt6_hold(fib_work->fen6_info.rt);
4854 break;
583419fd
IS
4855 case FIB_EVENT_RULE_ADD: /* fall through */
4856 case FIB_EVENT_RULE_DEL:
4857 memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info));
4858 fib_rule_get(fib_work->fr_info.rule);
4859 break;
4860 }
66a5763a
IS
4861}
4862
3057224e
IS
4863/* Called with rcu_read_lock() */
4864static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
4865 unsigned long event, void *ptr)
4866{
3057224e
IS
4867 struct mlxsw_sp_fib_event_work *fib_work;
4868 struct fib_notifier_info *info = ptr;
7e39d115 4869 struct mlxsw_sp_router *router;
3057224e 4870
8e29f979
IS
4871 if (!net_eq(info->net, &init_net) ||
4872 (info->family != AF_INET && info->family != AF_INET6))
3057224e
IS
4873 return NOTIFY_DONE;
4874
4875 fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC);
4876 if (WARN_ON(!fib_work))
4877 return NOTIFY_BAD;
4878
7e39d115
IS
4879 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
4880 fib_work->mlxsw_sp = router->mlxsw_sp;
3057224e
IS
4881 fib_work->event = event;
4882
66a5763a
IS
4883 switch (info->family) {
4884 case AF_INET:
4885 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib4_event_work);
4886 mlxsw_sp_router_fib4_event(fib_work, info);
3057224e 4887 break;
66a5763a
IS
4888 case AF_INET6:
4889 INIT_WORK(&fib_work->work, mlxsw_sp_router_fib6_event_work);
4890 mlxsw_sp_router_fib6_event(fib_work, info);
ad178c8e 4891 break;
3057224e
IS
4892 }
4893
a0e4761d 4894 mlxsw_core_schedule_work(&fib_work->work);
3057224e 4895
b45f64d1
JP
4896 return NOTIFY_DONE;
4897}
4898
4724ba56
IS
4899static struct mlxsw_sp_rif *
4900mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
4901 const struct net_device *dev)
4902{
4903 int i;
4904
4905 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
5f9efffb
IS
4906 if (mlxsw_sp->router->rifs[i] &&
4907 mlxsw_sp->router->rifs[i]->dev == dev)
4908 return mlxsw_sp->router->rifs[i];
4724ba56
IS
4909
4910 return NULL;
4911}
4912
4913static int mlxsw_sp_router_rif_disable(struct mlxsw_sp *mlxsw_sp, u16 rif)
4914{
4915 char ritr_pl[MLXSW_REG_RITR_LEN];
4916 int err;
4917
4918 mlxsw_reg_ritr_rif_pack(ritr_pl, rif);
4919 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4920 if (WARN_ON_ONCE(err))
4921 return err;
4922
4923 mlxsw_reg_ritr_enable_set(ritr_pl, false);
4924 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
4925}
4926
4927static void mlxsw_sp_router_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
bf95233e 4928 struct mlxsw_sp_rif *rif)
4724ba56 4929{
bf95233e
AS
4930 mlxsw_sp_router_rif_disable(mlxsw_sp, rif->rif_index);
4931 mlxsw_sp_nexthop_rif_gone_sync(mlxsw_sp, rif);
4932 mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, rif);
4724ba56
IS
4933}
4934
5ea1237f
AS
4935static bool
4936mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *rif, struct net_device *dev,
4937 unsigned long event)
4724ba56 4938{
5ea1237f
AS
4939 struct inet6_dev *inet6_dev;
4940 bool addr_list_empty = true;
4941 struct in_device *idev;
4942
4724ba56
IS
4943 switch (event) {
4944 case NETDEV_UP:
f1b1f273 4945 return rif == NULL;
4724ba56 4946 case NETDEV_DOWN:
5ea1237f
AS
4947 idev = __in_dev_get_rtnl(dev);
4948 if (idev && idev->ifa_list)
4949 addr_list_empty = false;
4950
4951 inet6_dev = __in6_dev_get(dev);
4952 if (addr_list_empty && inet6_dev &&
4953 !list_empty(&inet6_dev->addr_list))
4954 addr_list_empty = false;
4955
4956 if (rif && addr_list_empty &&
bf95233e 4957 !netif_is_l3_slave(rif->dev))
4724ba56
IS
4958 return true;
4959 /* It is possible we already removed the RIF ourselves
4960 * if it was assigned to a netdev that is now a bridge
4961 * or LAG slave.
4962 */
4963 return false;
4964 }
4965
4966 return false;
4967}
4968
e4f3c1c1
IS
4969static enum mlxsw_sp_rif_type
4970mlxsw_sp_dev_rif_type(const struct mlxsw_sp *mlxsw_sp,
4971 const struct net_device *dev)
4972{
4973 enum mlxsw_sp_fid_type type;
4974
6ddb7426
PM
4975 if (mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, NULL))
4976 return MLXSW_SP_RIF_TYPE_IPIP_LB;
4977
4978 /* Otherwise RIF type is derived from the type of the underlying FID. */
e4f3c1c1
IS
4979 if (is_vlan_dev(dev) && netif_is_bridge_master(vlan_dev_real_dev(dev)))
4980 type = MLXSW_SP_FID_TYPE_8021Q;
4981 else if (netif_is_bridge_master(dev) && br_vlan_enabled(dev))
4982 type = MLXSW_SP_FID_TYPE_8021Q;
4983 else if (netif_is_bridge_master(dev))
4984 type = MLXSW_SP_FID_TYPE_8021D;
4985 else
4986 type = MLXSW_SP_FID_TYPE_RFID;
4987
4988 return mlxsw_sp_fid_type_rif_type(mlxsw_sp, type);
4989}
4990
de5ed99e 4991static int mlxsw_sp_rif_index_alloc(struct mlxsw_sp *mlxsw_sp, u16 *p_rif_index)
4724ba56
IS
4992{
4993 int i;
4994
de5ed99e
IS
4995 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) {
4996 if (!mlxsw_sp->router->rifs[i]) {
4997 *p_rif_index = i;
4998 return 0;
4999 }
5000 }
4724ba56 5001
de5ed99e 5002 return -ENOBUFS;
4724ba56
IS
5003}
5004
e4f3c1c1
IS
5005static struct mlxsw_sp_rif *mlxsw_sp_rif_alloc(size_t rif_size, u16 rif_index,
5006 u16 vr_id,
5007 struct net_device *l3_dev)
4724ba56 5008{
bf95233e 5009 struct mlxsw_sp_rif *rif;
4724ba56 5010
e4f3c1c1 5011 rif = kzalloc(rif_size, GFP_KERNEL);
bf95233e 5012 if (!rif)
4724ba56
IS
5013 return NULL;
5014
bf95233e
AS
5015 INIT_LIST_HEAD(&rif->nexthop_list);
5016 INIT_LIST_HEAD(&rif->neigh_list);
5017 ether_addr_copy(rif->addr, l3_dev->dev_addr);
5018 rif->mtu = l3_dev->mtu;
5019 rif->vr_id = vr_id;
5020 rif->dev = l3_dev;
5021 rif->rif_index = rif_index;
4724ba56 5022
bf95233e 5023 return rif;
4724ba56
IS
5024}
5025
5f9efffb
IS
5026struct mlxsw_sp_rif *mlxsw_sp_rif_by_index(const struct mlxsw_sp *mlxsw_sp,
5027 u16 rif_index)
5028{
5029 return mlxsw_sp->router->rifs[rif_index];
5030}
5031
fd1b9d41
AS
5032u16 mlxsw_sp_rif_index(const struct mlxsw_sp_rif *rif)
5033{
5034 return rif->rif_index;
5035}
5036
92107cfb
PM
5037u16 mlxsw_sp_ipip_lb_rif_index(const struct mlxsw_sp_rif_ipip_lb *lb_rif)
5038{
5039 return lb_rif->common.rif_index;
5040}
5041
5042u16 mlxsw_sp_ipip_lb_ul_vr_id(const struct mlxsw_sp_rif_ipip_lb *lb_rif)
5043{
5044 return lb_rif->ul_vr_id;
5045}
5046
fd1b9d41
AS
5047int mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif)
5048{
5049 return rif->dev->ifindex;
5050}
5051
4724ba56 5052static struct mlxsw_sp_rif *
e4f3c1c1
IS
5053mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
5054 const struct mlxsw_sp_rif_params *params)
4724ba56 5055{
e4f3c1c1
IS
5056 u32 tb_id = l3mdev_fib_table(params->dev);
5057 const struct mlxsw_sp_rif_ops *ops;
010cadf9 5058 struct mlxsw_sp_fid *fid = NULL;
e4f3c1c1 5059 enum mlxsw_sp_rif_type type;
bf95233e 5060 struct mlxsw_sp_rif *rif;
a1107487
IS
5061 struct mlxsw_sp_vr *vr;
5062 u16 rif_index;
4724ba56
IS
5063 int err;
5064
e4f3c1c1
IS
5065 type = mlxsw_sp_dev_rif_type(mlxsw_sp, params->dev);
5066 ops = mlxsw_sp->router->rif_ops_arr[type];
5067
c9ec53f0
IS
5068 vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id ? : RT_TABLE_MAIN);
5069 if (IS_ERR(vr))
5070 return ERR_CAST(vr);
5071
de5ed99e
IS
5072 err = mlxsw_sp_rif_index_alloc(mlxsw_sp, &rif_index);
5073 if (err)
5074 goto err_rif_index_alloc;
4724ba56 5075
e4f3c1c1 5076 rif = mlxsw_sp_rif_alloc(ops->rif_size, rif_index, vr->id, params->dev);
a13a594d
IS
5077 if (!rif) {
5078 err = -ENOMEM;
5079 goto err_rif_alloc;
5080 }
e4f3c1c1
IS
5081 rif->mlxsw_sp = mlxsw_sp;
5082 rif->ops = ops;
a13a594d 5083
010cadf9
PM
5084 if (ops->fid_get) {
5085 fid = ops->fid_get(rif);
5086 if (IS_ERR(fid)) {
5087 err = PTR_ERR(fid);
5088 goto err_fid_get;
5089 }
5090 rif->fid = fid;
4d93ceeb
IS
5091 }
5092
e4f3c1c1
IS
5093 if (ops->setup)
5094 ops->setup(rif, params);
5095
5096 err = ops->configure(rif);
4724ba56 5097 if (err)
e4f3c1c1 5098 goto err_configure;
4724ba56 5099
e4f3c1c1 5100 mlxsw_sp_rif_counters_alloc(rif);
5f9efffb 5101 mlxsw_sp->router->rifs[rif_index] = rif;
6913229e 5102 vr->rif_count++;
4724ba56 5103
bf95233e 5104 return rif;
4724ba56 5105
e4f3c1c1 5106err_configure:
010cadf9
PM
5107 if (fid)
5108 mlxsw_sp_fid_put(fid);
a1107487 5109err_fid_get:
e4f3c1c1
IS
5110 kfree(rif);
5111err_rif_alloc:
de5ed99e 5112err_rif_index_alloc:
c9ec53f0 5113 mlxsw_sp_vr_put(vr);
4724ba56
IS
5114 return ERR_PTR(err);
5115}
5116
e4f3c1c1 5117void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif)
4724ba56 5118{
e4f3c1c1
IS
5119 const struct mlxsw_sp_rif_ops *ops = rif->ops;
5120 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
a1107487 5121 struct mlxsw_sp_fid *fid = rif->fid;
e4f3c1c1 5122 struct mlxsw_sp_vr *vr;
4724ba56 5123
bf95233e 5124 mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif);
e4f3c1c1 5125 vr = &mlxsw_sp->router->vrs[rif->vr_id];
e0c0afd8 5126
6913229e 5127 vr->rif_count--;
e4f3c1c1 5128 mlxsw_sp->router->rifs[rif->rif_index] = NULL;
e4f3c1c1 5129 mlxsw_sp_rif_counters_free(rif);
e4f3c1c1 5130 ops->deconfigure(rif);
010cadf9
PM
5131 if (fid)
5132 /* Loopback RIFs are not associated with a FID. */
5133 mlxsw_sp_fid_put(fid);
e4f3c1c1 5134 kfree(rif);
c9ec53f0 5135 mlxsw_sp_vr_put(vr);
4724ba56
IS
5136}
5137
e4f3c1c1
IS
5138static void
5139mlxsw_sp_rif_subport_params_init(struct mlxsw_sp_rif_params *params,
5140 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
5141{
5142 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
5143
5144 params->vid = mlxsw_sp_port_vlan->vid;
5145 params->lag = mlxsw_sp_port->lagged;
5146 if (params->lag)
5147 params->lag_id = mlxsw_sp_port->lag_id;
5148 else
5149 params->system_port = mlxsw_sp_port->local_port;
5150}
5151
7cbecf24 5152static int
a1107487 5153mlxsw_sp_port_vlan_router_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
7cbecf24 5154 struct net_device *l3_dev)
4724ba56 5155{
7cbecf24 5156 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
1b8f09a0 5157 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
7cbecf24 5158 u16 vid = mlxsw_sp_port_vlan->vid;
bf95233e 5159 struct mlxsw_sp_rif *rif;
a1107487 5160 struct mlxsw_sp_fid *fid;
03ea01e9 5161 int err;
4724ba56 5162
1b8f09a0 5163 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
bf95233e 5164 if (!rif) {
e4f3c1c1
IS
5165 struct mlxsw_sp_rif_params params = {
5166 .dev = l3_dev,
5167 };
5168
5169 mlxsw_sp_rif_subport_params_init(&params, mlxsw_sp_port_vlan);
5170 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
bf95233e
AS
5171 if (IS_ERR(rif))
5172 return PTR_ERR(rif);
4724ba56
IS
5173 }
5174
a1107487 5175 /* FID was already created, just take a reference */
e4f3c1c1 5176 fid = rif->ops->fid_get(rif);
a1107487
IS
5177 err = mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port, vid);
5178 if (err)
5179 goto err_fid_port_vid_map;
5180
7cbecf24 5181 err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false);
03ea01e9
IS
5182 if (err)
5183 goto err_port_vid_learning_set;
5184
7cbecf24 5185 err = mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid,
03ea01e9
IS
5186 BR_STATE_FORWARDING);
5187 if (err)
5188 goto err_port_vid_stp_set;
5189
a1107487 5190 mlxsw_sp_port_vlan->fid = fid;
4724ba56 5191
4724ba56 5192 return 0;
03ea01e9
IS
5193
5194err_port_vid_stp_set:
7cbecf24 5195 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
03ea01e9 5196err_port_vid_learning_set:
a1107487
IS
5197 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
5198err_fid_port_vid_map:
5199 mlxsw_sp_fid_put(fid);
03ea01e9 5200 return err;
4724ba56
IS
5201}
5202
a1107487
IS
5203void
5204mlxsw_sp_port_vlan_router_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
4724ba56 5205{
ce95e154 5206 struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
7cbecf24 5207 struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid;
ce95e154 5208 u16 vid = mlxsw_sp_port_vlan->vid;
ce95e154 5209
a1107487
IS
5210 if (WARN_ON(mlxsw_sp_fid_type(fid) != MLXSW_SP_FID_TYPE_RFID))
5211 return;
4aafc368 5212
a1107487 5213 mlxsw_sp_port_vlan->fid = NULL;
7cbecf24
IS
5214 mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, BR_STATE_BLOCKING);
5215 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
a1107487
IS
5216 mlxsw_sp_fid_port_vid_unmap(fid, mlxsw_sp_port, vid);
5217 /* If router port holds the last reference on the rFID, then the
5218 * associated Sub-port RIF will be destroyed.
5219 */
5220 mlxsw_sp_fid_put(fid);
4724ba56
IS
5221}
5222
7cbecf24
IS
5223static int mlxsw_sp_inetaddr_port_vlan_event(struct net_device *l3_dev,
5224 struct net_device *port_dev,
5225 unsigned long event, u16 vid)
4724ba56
IS
5226{
5227 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(port_dev);
ce95e154 5228 struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
4724ba56 5229
ce95e154 5230 mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
7cbecf24
IS
5231 if (WARN_ON(!mlxsw_sp_port_vlan))
5232 return -EINVAL;
4724ba56
IS
5233
5234 switch (event) {
5235 case NETDEV_UP:
a1107487 5236 return mlxsw_sp_port_vlan_router_join(mlxsw_sp_port_vlan,
7cbecf24 5237 l3_dev);
4724ba56 5238 case NETDEV_DOWN:
a1107487 5239 mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);
4724ba56
IS
5240 break;
5241 }
5242
5243 return 0;
5244}
5245
5246static int mlxsw_sp_inetaddr_port_event(struct net_device *port_dev,
5247 unsigned long event)
5248{
2b94e58d
JP
5249 if (netif_is_bridge_port(port_dev) ||
5250 netif_is_lag_port(port_dev) ||
5251 netif_is_ovs_port(port_dev))
4724ba56
IS
5252 return 0;
5253
7cbecf24 5254 return mlxsw_sp_inetaddr_port_vlan_event(port_dev, port_dev, event, 1);
4724ba56
IS
5255}
5256
5257static int __mlxsw_sp_inetaddr_lag_event(struct net_device *l3_dev,
5258 struct net_device *lag_dev,
5259 unsigned long event, u16 vid)
5260{
5261 struct net_device *port_dev;
5262 struct list_head *iter;
5263 int err;
5264
5265 netdev_for_each_lower_dev(lag_dev, port_dev, iter) {
5266 if (mlxsw_sp_port_dev_check(port_dev)) {
7cbecf24
IS
5267 err = mlxsw_sp_inetaddr_port_vlan_event(l3_dev,
5268 port_dev,
5269 event, vid);
4724ba56
IS
5270 if (err)
5271 return err;
5272 }
5273 }
5274
5275 return 0;
5276}
5277
5278static int mlxsw_sp_inetaddr_lag_event(struct net_device *lag_dev,
5279 unsigned long event)
5280{
5281 if (netif_is_bridge_port(lag_dev))
5282 return 0;
5283
5284 return __mlxsw_sp_inetaddr_lag_event(lag_dev, lag_dev, event, 1);
5285}
5286
4724ba56 5287static int mlxsw_sp_inetaddr_bridge_event(struct net_device *l3_dev,
4724ba56
IS
5288 unsigned long event)
5289{
5290 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
e4f3c1c1
IS
5291 struct mlxsw_sp_rif_params params = {
5292 .dev = l3_dev,
5293 };
a1107487 5294 struct mlxsw_sp_rif *rif;
4724ba56
IS
5295
5296 switch (event) {
5297 case NETDEV_UP:
e4f3c1c1
IS
5298 rif = mlxsw_sp_rif_create(mlxsw_sp, &params);
5299 if (IS_ERR(rif))
5300 return PTR_ERR(rif);
5301 break;
4724ba56 5302 case NETDEV_DOWN:
a1107487 5303 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
e4f3c1c1 5304 mlxsw_sp_rif_destroy(rif);
4724ba56
IS
5305 break;
5306 }
5307
5308 return 0;
5309}
5310
5311static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev,
5312 unsigned long event)
5313{
5314 struct net_device *real_dev = vlan_dev_real_dev(vlan_dev);
4724ba56
IS
5315 u16 vid = vlan_dev_vlan_id(vlan_dev);
5316
6b27c8ad
IS
5317 if (netif_is_bridge_port(vlan_dev))
5318 return 0;
5319
4724ba56 5320 if (mlxsw_sp_port_dev_check(real_dev))
7cbecf24
IS
5321 return mlxsw_sp_inetaddr_port_vlan_event(vlan_dev, real_dev,
5322 event, vid);
4724ba56
IS
5323 else if (netif_is_lag_master(real_dev))
5324 return __mlxsw_sp_inetaddr_lag_event(vlan_dev, real_dev, event,
5325 vid);
c57529e1 5326 else if (netif_is_bridge_master(real_dev) && br_vlan_enabled(real_dev))
a1107487 5327 return mlxsw_sp_inetaddr_bridge_event(vlan_dev, event);
4724ba56
IS
5328
5329 return 0;
5330}
5331
b1e45526
IS
5332static int __mlxsw_sp_inetaddr_event(struct net_device *dev,
5333 unsigned long event)
5334{
5335 if (mlxsw_sp_port_dev_check(dev))
5336 return mlxsw_sp_inetaddr_port_event(dev, event);
5337 else if (netif_is_lag_master(dev))
5338 return mlxsw_sp_inetaddr_lag_event(dev, event);
5339 else if (netif_is_bridge_master(dev))
a1107487 5340 return mlxsw_sp_inetaddr_bridge_event(dev, event);
b1e45526
IS
5341 else if (is_vlan_dev(dev))
5342 return mlxsw_sp_inetaddr_vlan_event(dev, event);
5343 else
5344 return 0;
5345}
5346
4724ba56
IS
5347int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
5348 unsigned long event, void *ptr)
5349{
5350 struct in_ifaddr *ifa = (struct in_ifaddr *) ptr;
5351 struct net_device *dev = ifa->ifa_dev->dev;
5352 struct mlxsw_sp *mlxsw_sp;
bf95233e 5353 struct mlxsw_sp_rif *rif;
4724ba56
IS
5354 int err = 0;
5355
5356 mlxsw_sp = mlxsw_sp_lower_get(dev);
5357 if (!mlxsw_sp)
5358 goto out;
5359
bf95233e 5360 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
5ea1237f 5361 if (!mlxsw_sp_rif_should_config(rif, dev, event))
4724ba56
IS
5362 goto out;
5363
b1e45526 5364 err = __mlxsw_sp_inetaddr_event(dev, event);
4724ba56
IS
5365out:
5366 return notifier_from_errno(err);
5367}
5368
5ea1237f
AS
5369struct mlxsw_sp_inet6addr_event_work {
5370 struct work_struct work;
5371 struct net_device *dev;
5372 unsigned long event;
5373};
5374
5375static void mlxsw_sp_inet6addr_event_work(struct work_struct *work)
5376{
5377 struct mlxsw_sp_inet6addr_event_work *inet6addr_work =
5378 container_of(work, struct mlxsw_sp_inet6addr_event_work, work);
5379 struct net_device *dev = inet6addr_work->dev;
5380 unsigned long event = inet6addr_work->event;
5381 struct mlxsw_sp *mlxsw_sp;
5382 struct mlxsw_sp_rif *rif;
5383
5384 rtnl_lock();
5385 mlxsw_sp = mlxsw_sp_lower_get(dev);
5386 if (!mlxsw_sp)
5387 goto out;
5388
5389 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
5390 if (!mlxsw_sp_rif_should_config(rif, dev, event))
5391 goto out;
5392
5393 __mlxsw_sp_inetaddr_event(dev, event);
5394out:
5395 rtnl_unlock();
5396 dev_put(dev);
5397 kfree(inet6addr_work);
5398}
5399
5400/* Called with rcu_read_lock() */
5401int mlxsw_sp_inet6addr_event(struct notifier_block *unused,
5402 unsigned long event, void *ptr)
5403{
5404 struct inet6_ifaddr *if6 = (struct inet6_ifaddr *) ptr;
5405 struct mlxsw_sp_inet6addr_event_work *inet6addr_work;
5406 struct net_device *dev = if6->idev->dev;
5407
5408 if (!mlxsw_sp_port_dev_lower_find_rcu(dev))
5409 return NOTIFY_DONE;
5410
5411 inet6addr_work = kzalloc(sizeof(*inet6addr_work), GFP_ATOMIC);
5412 if (!inet6addr_work)
5413 return NOTIFY_BAD;
5414
5415 INIT_WORK(&inet6addr_work->work, mlxsw_sp_inet6addr_event_work);
5416 inet6addr_work->dev = dev;
5417 inet6addr_work->event = event;
5418 dev_hold(dev);
5419 mlxsw_core_schedule_work(&inet6addr_work->work);
5420
5421 return NOTIFY_DONE;
5422}
5423
bf95233e 5424static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
4724ba56
IS
5425 const char *mac, int mtu)
5426{
5427 char ritr_pl[MLXSW_REG_RITR_LEN];
5428 int err;
5429
bf95233e 5430 mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
4724ba56
IS
5431 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5432 if (err)
5433 return err;
5434
5435 mlxsw_reg_ritr_mtu_set(ritr_pl, mtu);
5436 mlxsw_reg_ritr_if_mac_memcpy_to(ritr_pl, mac);
5437 mlxsw_reg_ritr_op_set(ritr_pl, MLXSW_REG_RITR_RIF_CREATE);
5438 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5439}
5440
5441int mlxsw_sp_netdevice_router_port_event(struct net_device *dev)
5442{
5443 struct mlxsw_sp *mlxsw_sp;
bf95233e 5444 struct mlxsw_sp_rif *rif;
a1107487 5445 u16 fid_index;
4724ba56
IS
5446 int err;
5447
5448 mlxsw_sp = mlxsw_sp_lower_get(dev);
5449 if (!mlxsw_sp)
5450 return 0;
5451
bf95233e
AS
5452 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
5453 if (!rif)
4724ba56 5454 return 0;
a1107487 5455 fid_index = mlxsw_sp_fid_index(rif->fid);
4724ba56 5456
a1107487 5457 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, false);
4724ba56
IS
5458 if (err)
5459 return err;
5460
bf95233e
AS
5461 err = mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, dev->dev_addr,
5462 dev->mtu);
4724ba56
IS
5463 if (err)
5464 goto err_rif_edit;
5465
a1107487 5466 err = mlxsw_sp_rif_fdb_op(mlxsw_sp, dev->dev_addr, fid_index, true);
4724ba56
IS
5467 if (err)
5468 goto err_rif_fdb_op;
5469
bf95233e
AS
5470 ether_addr_copy(rif->addr, dev->dev_addr);
5471 rif->mtu = dev->mtu;
4724ba56 5472
bf95233e 5473 netdev_dbg(dev, "Updated RIF=%d\n", rif->rif_index);
4724ba56
IS
5474
5475 return 0;
5476
5477err_rif_fdb_op:
bf95233e 5478 mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, rif->addr, rif->mtu);
4724ba56 5479err_rif_edit:
a1107487 5480 mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, true);
4724ba56
IS
5481 return err;
5482}
5483
b1e45526
IS
5484static int mlxsw_sp_port_vrf_join(struct mlxsw_sp *mlxsw_sp,
5485 struct net_device *l3_dev)
7179eb5a 5486{
b1e45526 5487 struct mlxsw_sp_rif *rif;
7179eb5a 5488
b1e45526
IS
5489 /* If netdev is already associated with a RIF, then we need to
5490 * destroy it and create a new one with the new virtual router ID.
7179eb5a 5491 */
b1e45526
IS
5492 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
5493 if (rif)
5494 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
7179eb5a 5495
b1e45526 5496 return __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_UP);
7179eb5a
IS
5497}
5498
b1e45526
IS
5499static void mlxsw_sp_port_vrf_leave(struct mlxsw_sp *mlxsw_sp,
5500 struct net_device *l3_dev)
7179eb5a 5501{
b1e45526 5502 struct mlxsw_sp_rif *rif;
7179eb5a 5503
b1e45526
IS
5504 rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
5505 if (!rif)
7179eb5a 5506 return;
b1e45526 5507 __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
7179eb5a
IS
5508}
5509
b1e45526
IS
5510int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
5511 struct netdev_notifier_changeupper_info *info)
3d70e458 5512{
b1e45526
IS
5513 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
5514 int err = 0;
3d70e458 5515
b1e45526
IS
5516 if (!mlxsw_sp)
5517 return 0;
3d70e458 5518
b1e45526
IS
5519 switch (event) {
5520 case NETDEV_PRECHANGEUPPER:
5521 return 0;
5522 case NETDEV_CHANGEUPPER:
5523 if (info->linking)
5524 err = mlxsw_sp_port_vrf_join(mlxsw_sp, l3_dev);
5525 else
5526 mlxsw_sp_port_vrf_leave(mlxsw_sp, l3_dev);
5527 break;
5528 }
3d70e458 5529
b1e45526 5530 return err;
3d70e458
IS
5531}
5532
e4f3c1c1
IS
5533static struct mlxsw_sp_rif_subport *
5534mlxsw_sp_rif_subport_rif(const struct mlxsw_sp_rif *rif)
a1107487 5535{
e4f3c1c1
IS
5536 return container_of(rif, struct mlxsw_sp_rif_subport, common);
5537}
5538
5539static void mlxsw_sp_rif_subport_setup(struct mlxsw_sp_rif *rif,
5540 const struct mlxsw_sp_rif_params *params)
5541{
5542 struct mlxsw_sp_rif_subport *rif_subport;
5543
5544 rif_subport = mlxsw_sp_rif_subport_rif(rif);
5545 rif_subport->vid = params->vid;
5546 rif_subport->lag = params->lag;
5547 if (params->lag)
5548 rif_subport->lag_id = params->lag_id;
a1107487 5549 else
e4f3c1c1
IS
5550 rif_subport->system_port = params->system_port;
5551}
5552
5553static int mlxsw_sp_rif_subport_op(struct mlxsw_sp_rif *rif, bool enable)
5554{
5555 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5556 struct mlxsw_sp_rif_subport *rif_subport;
5557 char ritr_pl[MLXSW_REG_RITR_LEN];
5558
5559 rif_subport = mlxsw_sp_rif_subport_rif(rif);
5560 mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_SP_IF,
9571e828
PM
5561 rif->rif_index, rif->vr_id, rif->dev->mtu);
5562 mlxsw_reg_ritr_mac_pack(ritr_pl, rif->dev->dev_addr);
e4f3c1c1
IS
5563 mlxsw_reg_ritr_sp_if_pack(ritr_pl, rif_subport->lag,
5564 rif_subport->lag ? rif_subport->lag_id :
5565 rif_subport->system_port,
5566 rif_subport->vid);
5567
5568 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5569}
5570
5571static int mlxsw_sp_rif_subport_configure(struct mlxsw_sp_rif *rif)
5572{
010cadf9
PM
5573 int err;
5574
5575 err = mlxsw_sp_rif_subport_op(rif, true);
5576 if (err)
5577 return err;
5578
5579 err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5580 mlxsw_sp_fid_index(rif->fid), true);
5581 if (err)
5582 goto err_rif_fdb_op;
5583
5584 mlxsw_sp_fid_rif_set(rif->fid, rif);
5585 return 0;
5586
5587err_rif_fdb_op:
5588 mlxsw_sp_rif_subport_op(rif, false);
5589 return err;
a1107487
IS
5590}
5591
e4f3c1c1
IS
5592static void mlxsw_sp_rif_subport_deconfigure(struct mlxsw_sp_rif *rif)
5593{
010cadf9
PM
5594 struct mlxsw_sp_fid *fid = rif->fid;
5595
5596 mlxsw_sp_fid_rif_set(fid, NULL);
5597 mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5598 mlxsw_sp_fid_index(fid), false);
e4f3c1c1
IS
5599 mlxsw_sp_rif_subport_op(rif, false);
5600}
5601
5602static struct mlxsw_sp_fid *
5603mlxsw_sp_rif_subport_fid_get(struct mlxsw_sp_rif *rif)
5604{
5605 return mlxsw_sp_fid_rfid_get(rif->mlxsw_sp, rif->rif_index);
5606}
5607
5608static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_subport_ops = {
5609 .type = MLXSW_SP_RIF_TYPE_SUBPORT,
5610 .rif_size = sizeof(struct mlxsw_sp_rif_subport),
5611 .setup = mlxsw_sp_rif_subport_setup,
5612 .configure = mlxsw_sp_rif_subport_configure,
5613 .deconfigure = mlxsw_sp_rif_subport_deconfigure,
5614 .fid_get = mlxsw_sp_rif_subport_fid_get,
5615};
5616
5617static int mlxsw_sp_rif_vlan_fid_op(struct mlxsw_sp_rif *rif,
5618 enum mlxsw_reg_ritr_if_type type,
5619 u16 vid_fid, bool enable)
5620{
5621 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5622 char ritr_pl[MLXSW_REG_RITR_LEN];
5623
5624 mlxsw_reg_ritr_pack(ritr_pl, enable, type, rif->rif_index, rif->vr_id,
9571e828
PM
5625 rif->dev->mtu);
5626 mlxsw_reg_ritr_mac_pack(ritr_pl, rif->dev->dev_addr);
e4f3c1c1
IS
5627 mlxsw_reg_ritr_fid_set(ritr_pl, type, vid_fid);
5628
5629 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5630}
5631
5632static u8 mlxsw_sp_router_port(const struct mlxsw_sp *mlxsw_sp)
5633{
5634 return mlxsw_core_max_ports(mlxsw_sp->core) + 1;
5635}
5636
5637static int mlxsw_sp_rif_vlan_configure(struct mlxsw_sp_rif *rif)
5638{
5639 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5640 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
5641 int err;
5642
5643 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, true);
5644 if (err)
5645 return err;
5646
0d284818
IS
5647 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5648 mlxsw_sp_router_port(mlxsw_sp), true);
5649 if (err)
5650 goto err_fid_mc_flood_set;
5651
e4f3c1c1
IS
5652 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5653 mlxsw_sp_router_port(mlxsw_sp), true);
5654 if (err)
5655 goto err_fid_bc_flood_set;
5656
010cadf9
PM
5657 err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5658 mlxsw_sp_fid_index(rif->fid), true);
5659 if (err)
5660 goto err_rif_fdb_op;
5661
5662 mlxsw_sp_fid_rif_set(rif->fid, rif);
e4f3c1c1
IS
5663 return 0;
5664
010cadf9
PM
5665err_rif_fdb_op:
5666 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5667 mlxsw_sp_router_port(mlxsw_sp), false);
e4f3c1c1 5668err_fid_bc_flood_set:
0d284818
IS
5669 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5670 mlxsw_sp_router_port(mlxsw_sp), false);
5671err_fid_mc_flood_set:
e4f3c1c1
IS
5672 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
5673 return err;
5674}
5675
5676static void mlxsw_sp_rif_vlan_deconfigure(struct mlxsw_sp_rif *rif)
5677{
e4f3c1c1 5678 u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
010cadf9
PM
5679 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5680 struct mlxsw_sp_fid *fid = rif->fid;
e4f3c1c1 5681
010cadf9
PM
5682 mlxsw_sp_fid_rif_set(fid, NULL);
5683 mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5684 mlxsw_sp_fid_index(fid), false);
e4f3c1c1
IS
5685 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5686 mlxsw_sp_router_port(mlxsw_sp), false);
0d284818
IS
5687 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5688 mlxsw_sp_router_port(mlxsw_sp), false);
e4f3c1c1
IS
5689 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false);
5690}
5691
5692static struct mlxsw_sp_fid *
5693mlxsw_sp_rif_vlan_fid_get(struct mlxsw_sp_rif *rif)
5694{
5695 u16 vid = is_vlan_dev(rif->dev) ? vlan_dev_vlan_id(rif->dev) : 1;
5696
5697 return mlxsw_sp_fid_8021q_get(rif->mlxsw_sp, vid);
5698}
5699
5700static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_vlan_ops = {
5701 .type = MLXSW_SP_RIF_TYPE_VLAN,
5702 .rif_size = sizeof(struct mlxsw_sp_rif),
5703 .configure = mlxsw_sp_rif_vlan_configure,
5704 .deconfigure = mlxsw_sp_rif_vlan_deconfigure,
5705 .fid_get = mlxsw_sp_rif_vlan_fid_get,
5706};
5707
5708static int mlxsw_sp_rif_fid_configure(struct mlxsw_sp_rif *rif)
5709{
5710 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5711 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
5712 int err;
5713
5714 err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index,
5715 true);
5716 if (err)
5717 return err;
5718
0d284818
IS
5719 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5720 mlxsw_sp_router_port(mlxsw_sp), true);
5721 if (err)
5722 goto err_fid_mc_flood_set;
5723
e4f3c1c1
IS
5724 err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5725 mlxsw_sp_router_port(mlxsw_sp), true);
5726 if (err)
5727 goto err_fid_bc_flood_set;
5728
010cadf9
PM
5729 err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5730 mlxsw_sp_fid_index(rif->fid), true);
5731 if (err)
5732 goto err_rif_fdb_op;
5733
5734 mlxsw_sp_fid_rif_set(rif->fid, rif);
e4f3c1c1
IS
5735 return 0;
5736
010cadf9
PM
5737err_rif_fdb_op:
5738 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5739 mlxsw_sp_router_port(mlxsw_sp), false);
e4f3c1c1 5740err_fid_bc_flood_set:
0d284818
IS
5741 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5742 mlxsw_sp_router_port(mlxsw_sp), false);
5743err_fid_mc_flood_set:
e4f3c1c1
IS
5744 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
5745 return err;
5746}
5747
5748static void mlxsw_sp_rif_fid_deconfigure(struct mlxsw_sp_rif *rif)
5749{
e4f3c1c1 5750 u16 fid_index = mlxsw_sp_fid_index(rif->fid);
010cadf9
PM
5751 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5752 struct mlxsw_sp_fid *fid = rif->fid;
e4f3c1c1 5753
010cadf9
PM
5754 mlxsw_sp_fid_rif_set(fid, NULL);
5755 mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
5756 mlxsw_sp_fid_index(fid), false);
e4f3c1c1
IS
5757 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
5758 mlxsw_sp_router_port(mlxsw_sp), false);
0d284818
IS
5759 mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
5760 mlxsw_sp_router_port(mlxsw_sp), false);
e4f3c1c1
IS
5761 mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false);
5762}
5763
5764static struct mlxsw_sp_fid *
5765mlxsw_sp_rif_fid_fid_get(struct mlxsw_sp_rif *rif)
5766{
5767 return mlxsw_sp_fid_8021d_get(rif->mlxsw_sp, rif->dev->ifindex);
5768}
5769
5770static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_fid_ops = {
5771 .type = MLXSW_SP_RIF_TYPE_FID,
5772 .rif_size = sizeof(struct mlxsw_sp_rif),
5773 .configure = mlxsw_sp_rif_fid_configure,
5774 .deconfigure = mlxsw_sp_rif_fid_deconfigure,
5775 .fid_get = mlxsw_sp_rif_fid_fid_get,
5776};
5777
6ddb7426
PM
5778static struct mlxsw_sp_rif_ipip_lb *
5779mlxsw_sp_rif_ipip_lb_rif(struct mlxsw_sp_rif *rif)
5780{
5781 return container_of(rif, struct mlxsw_sp_rif_ipip_lb, common);
5782}
5783
5784static void
5785mlxsw_sp_rif_ipip_lb_setup(struct mlxsw_sp_rif *rif,
5786 const struct mlxsw_sp_rif_params *params)
5787{
5788 struct mlxsw_sp_rif_params_ipip_lb *params_lb;
5789 struct mlxsw_sp_rif_ipip_lb *rif_lb;
5790
5791 params_lb = container_of(params, struct mlxsw_sp_rif_params_ipip_lb,
5792 common);
5793 rif_lb = mlxsw_sp_rif_ipip_lb_rif(rif);
5794 rif_lb->lb_config = params_lb->lb_config;
5795}
5796
5797static int
5798mlxsw_sp_rif_ipip_lb_op(struct mlxsw_sp_rif_ipip_lb *lb_rif,
5799 struct mlxsw_sp_vr *ul_vr, bool enable)
5800{
5801 struct mlxsw_sp_rif_ipip_lb_config lb_cf = lb_rif->lb_config;
5802 struct mlxsw_sp_rif *rif = &lb_rif->common;
5803 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5804 char ritr_pl[MLXSW_REG_RITR_LEN];
5805 u32 saddr4;
5806
5807 switch (lb_cf.ul_protocol) {
5808 case MLXSW_SP_L3_PROTO_IPV4:
5809 saddr4 = be32_to_cpu(lb_cf.saddr.addr4);
5810 mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_LOOPBACK_IF,
5811 rif->rif_index, rif->vr_id, rif->dev->mtu);
5812 mlxsw_reg_ritr_loopback_ipip4_pack(ritr_pl, lb_cf.lb_ipipt,
5813 MLXSW_REG_RITR_LOOPBACK_IPIP_OPTIONS_GRE_KEY_PRESET,
5814 ul_vr->id, saddr4, lb_cf.okey);
5815 break;
5816
5817 case MLXSW_SP_L3_PROTO_IPV6:
5818 return -EAFNOSUPPORT;
5819 }
5820
5821 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
5822}
5823
5824static int
5825mlxsw_sp_rif_ipip_lb_configure(struct mlxsw_sp_rif *rif)
5826{
5827 struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif);
5828 u32 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(rif->dev);
5829 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5830 struct mlxsw_sp_vr *ul_vr;
5831 int err;
5832
5833 ul_vr = mlxsw_sp_vr_get(mlxsw_sp, ul_tb_id);
5834 if (IS_ERR(ul_vr))
5835 return PTR_ERR(ul_vr);
5836
5837 err = mlxsw_sp_rif_ipip_lb_op(lb_rif, ul_vr, true);
5838 if (err)
5839 goto err_loopback_op;
5840
5841 lb_rif->ul_vr_id = ul_vr->id;
5842 ++ul_vr->rif_count;
5843 return 0;
5844
5845err_loopback_op:
5846 mlxsw_sp_vr_put(ul_vr);
5847 return err;
5848}
5849
5850static void mlxsw_sp_rif_ipip_lb_deconfigure(struct mlxsw_sp_rif *rif)
5851{
5852 struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif);
5853 struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
5854 struct mlxsw_sp_vr *ul_vr;
5855
5856 ul_vr = &mlxsw_sp->router->vrs[lb_rif->ul_vr_id];
5857 mlxsw_sp_rif_ipip_lb_op(lb_rif, ul_vr, false);
5858
5859 --ul_vr->rif_count;
5860 mlxsw_sp_vr_put(ul_vr);
5861}
5862
5863static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_ipip_lb_ops = {
5864 .type = MLXSW_SP_RIF_TYPE_IPIP_LB,
5865 .rif_size = sizeof(struct mlxsw_sp_rif_ipip_lb),
5866 .setup = mlxsw_sp_rif_ipip_lb_setup,
5867 .configure = mlxsw_sp_rif_ipip_lb_configure,
5868 .deconfigure = mlxsw_sp_rif_ipip_lb_deconfigure,
5869};
5870
e4f3c1c1
IS
5871static const struct mlxsw_sp_rif_ops *mlxsw_sp_rif_ops_arr[] = {
5872 [MLXSW_SP_RIF_TYPE_SUBPORT] = &mlxsw_sp_rif_subport_ops,
5873 [MLXSW_SP_RIF_TYPE_VLAN] = &mlxsw_sp_rif_vlan_ops,
5874 [MLXSW_SP_RIF_TYPE_FID] = &mlxsw_sp_rif_fid_ops,
6ddb7426 5875 [MLXSW_SP_RIF_TYPE_IPIP_LB] = &mlxsw_sp_rif_ipip_lb_ops,
e4f3c1c1
IS
5876};
5877
348b8fc3
IS
5878static int mlxsw_sp_rifs_init(struct mlxsw_sp *mlxsw_sp)
5879{
5880 u64 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
5881
5882 mlxsw_sp->router->rifs = kcalloc(max_rifs,
5883 sizeof(struct mlxsw_sp_rif *),
5884 GFP_KERNEL);
5885 if (!mlxsw_sp->router->rifs)
5886 return -ENOMEM;
e4f3c1c1
IS
5887
5888 mlxsw_sp->router->rif_ops_arr = mlxsw_sp_rif_ops_arr;
5889
348b8fc3
IS
5890 return 0;
5891}
5892
5893static void mlxsw_sp_rifs_fini(struct mlxsw_sp *mlxsw_sp)
5894{
5895 int i;
5896
5897 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
5898 WARN_ON_ONCE(mlxsw_sp->router->rifs[i]);
5899
5900 kfree(mlxsw_sp->router->rifs);
5901}
5902
38ebc0f4
PM
5903static int mlxsw_sp_ipips_init(struct mlxsw_sp *mlxsw_sp)
5904{
5905 mlxsw_sp->router->ipip_ops_arr = mlxsw_sp_ipip_ops_arr;
1012b9ac 5906 INIT_LIST_HEAD(&mlxsw_sp->router->ipip_list);
38ebc0f4
PM
5907 return 0;
5908}
5909
5910static void mlxsw_sp_ipips_fini(struct mlxsw_sp *mlxsw_sp)
5911{
1012b9ac 5912 WARN_ON(!list_empty(&mlxsw_sp->router->ipip_list));
38ebc0f4
PM
5913}
5914
c3852ef7
IS
5915static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb)
5916{
7e39d115 5917 struct mlxsw_sp_router *router;
c3852ef7
IS
5918
5919 /* Flush pending FIB notifications and then flush the device's
5920 * table before requesting another dump. The FIB notification
5921 * block is unregistered, so no need to take RTNL.
5922 */
5923 mlxsw_core_flush_owq();
7e39d115
IS
5924 router = container_of(nb, struct mlxsw_sp_router, fib_nb);
5925 mlxsw_sp_router_fib_flush(router->mlxsw_sp);
c3852ef7
IS
5926}
5927
4724ba56
IS
5928static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
5929{
5930 char rgcr_pl[MLXSW_REG_RGCR_LEN];
5931 u64 max_rifs;
5932 int err;
5933
5934 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_RIFS))
5935 return -EIO;
4724ba56 5936 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
4724ba56 5937
e29237e7 5938 mlxsw_reg_rgcr_pack(rgcr_pl, true, true);
4724ba56
IS
5939 mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, max_rifs);
5940 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
5941 if (err)
348b8fc3 5942 return err;
4724ba56 5943 return 0;
4724ba56
IS
5944}
5945
5946static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
5947{
5948 char rgcr_pl[MLXSW_REG_RGCR_LEN];
4724ba56 5949
e29237e7 5950 mlxsw_reg_rgcr_pack(rgcr_pl, false, false);
4724ba56 5951 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
4724ba56
IS
5952}
5953
b45f64d1
JP
5954int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
5955{
9011b677 5956 struct mlxsw_sp_router *router;
b45f64d1
JP
5957 int err;
5958
9011b677
IS
5959 router = kzalloc(sizeof(*mlxsw_sp->router), GFP_KERNEL);
5960 if (!router)
5961 return -ENOMEM;
5962 mlxsw_sp->router = router;
5963 router->mlxsw_sp = mlxsw_sp;
5964
5965 INIT_LIST_HEAD(&mlxsw_sp->router->nexthop_neighs_list);
b45f64d1
JP
5966 err = __mlxsw_sp_router_init(mlxsw_sp);
5967 if (err)
9011b677 5968 goto err_router_init;
b45f64d1 5969
348b8fc3
IS
5970 err = mlxsw_sp_rifs_init(mlxsw_sp);
5971 if (err)
5972 goto err_rifs_init;
5973
38ebc0f4
PM
5974 err = mlxsw_sp_ipips_init(mlxsw_sp);
5975 if (err)
5976 goto err_ipips_init;
5977
9011b677 5978 err = rhashtable_init(&mlxsw_sp->router->nexthop_ht,
c53b8e1b
IS
5979 &mlxsw_sp_nexthop_ht_params);
5980 if (err)
5981 goto err_nexthop_ht_init;
5982
9011b677 5983 err = rhashtable_init(&mlxsw_sp->router->nexthop_group_ht,
e9ad5e7d
IS
5984 &mlxsw_sp_nexthop_group_ht_params);
5985 if (err)
5986 goto err_nexthop_group_ht_init;
5987
8494ab06
IS
5988 err = mlxsw_sp_lpm_init(mlxsw_sp);
5989 if (err)
5990 goto err_lpm_init;
5991
b45f64d1
JP
5992 err = mlxsw_sp_vrs_init(mlxsw_sp);
5993 if (err)
5994 goto err_vrs_init;
5995
8c9583a8 5996 err = mlxsw_sp_neigh_init(mlxsw_sp);
b45f64d1
JP
5997 if (err)
5998 goto err_neigh_init;
5999
7e39d115
IS
6000 mlxsw_sp->router->fib_nb.notifier_call = mlxsw_sp_router_fib_event;
6001 err = register_fib_notifier(&mlxsw_sp->router->fib_nb,
c3852ef7
IS
6002 mlxsw_sp_router_fib_dump_flush);
6003 if (err)
6004 goto err_register_fib_notifier;
6005
b45f64d1
JP
6006 return 0;
6007
c3852ef7
IS
6008err_register_fib_notifier:
6009 mlxsw_sp_neigh_fini(mlxsw_sp);
b45f64d1
JP
6010err_neigh_init:
6011 mlxsw_sp_vrs_fini(mlxsw_sp);
6012err_vrs_init:
8494ab06
IS
6013 mlxsw_sp_lpm_fini(mlxsw_sp);
6014err_lpm_init:
9011b677 6015 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
e9ad5e7d 6016err_nexthop_group_ht_init:
9011b677 6017 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
c53b8e1b 6018err_nexthop_ht_init:
38ebc0f4
PM
6019 mlxsw_sp_ipips_fini(mlxsw_sp);
6020err_ipips_init:
348b8fc3
IS
6021 mlxsw_sp_rifs_fini(mlxsw_sp);
6022err_rifs_init:
b45f64d1 6023 __mlxsw_sp_router_fini(mlxsw_sp);
9011b677
IS
6024err_router_init:
6025 kfree(mlxsw_sp->router);
b45f64d1
JP
6026 return err;
6027}
6028
6029void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
6030{
7e39d115 6031 unregister_fib_notifier(&mlxsw_sp->router->fib_nb);
b45f64d1
JP
6032 mlxsw_sp_neigh_fini(mlxsw_sp);
6033 mlxsw_sp_vrs_fini(mlxsw_sp);
8494ab06 6034 mlxsw_sp_lpm_fini(mlxsw_sp);
9011b677
IS
6035 rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht);
6036 rhashtable_destroy(&mlxsw_sp->router->nexthop_ht);
38ebc0f4 6037 mlxsw_sp_ipips_fini(mlxsw_sp);
348b8fc3 6038 mlxsw_sp_rifs_fini(mlxsw_sp);
b45f64d1 6039 __mlxsw_sp_router_fini(mlxsw_sp);
9011b677 6040 kfree(mlxsw_sp->router);
b45f64d1 6041}