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