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