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>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the names of the copyright holders nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
19 * Alternatively, this software may be distributed under the terms of the
20 * GNU General Public License ("GPL") version 2 as published by the Free
21 * Software Foundation.
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
36 #include <linux/kernel.h>
37 #include <linux/types.h>
38 #include <linux/rhashtable.h>
39 #include <linux/bitops.h>
40 #include <linux/in6.h>
46 #define mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) \
47 for_each_set_bit(prefix, (prefix_usage)->b, MLXSW_SP_PREFIX_COUNT)
50 mlxsw_sp_prefix_usage_subset(struct mlxsw_sp_prefix_usage *prefix_usage1,
51 struct mlxsw_sp_prefix_usage *prefix_usage2)
55 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage1) {
56 if (!test_bit(prefix, prefix_usage2->b))
63 mlxsw_sp_prefix_usage_eq(struct mlxsw_sp_prefix_usage *prefix_usage1,
64 struct mlxsw_sp_prefix_usage *prefix_usage2)
66 return !memcmp(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
70 mlxsw_sp_prefix_usage_none(struct mlxsw_sp_prefix_usage *prefix_usage)
72 struct mlxsw_sp_prefix_usage prefix_usage_none = {{ 0 } };
74 return mlxsw_sp_prefix_usage_eq(prefix_usage, &prefix_usage_none);
78 mlxsw_sp_prefix_usage_cpy(struct mlxsw_sp_prefix_usage *prefix_usage1,
79 struct mlxsw_sp_prefix_usage *prefix_usage2)
81 memcpy(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
85 mlxsw_sp_prefix_usage_zero(struct mlxsw_sp_prefix_usage *prefix_usage)
87 memset(prefix_usage, 0, sizeof(*prefix_usage));
91 mlxsw_sp_prefix_usage_set(struct mlxsw_sp_prefix_usage *prefix_usage,
92 unsigned char prefix_len)
94 set_bit(prefix_len, prefix_usage->b);
98 mlxsw_sp_prefix_usage_clear(struct mlxsw_sp_prefix_usage *prefix_usage,
99 unsigned char prefix_len)
101 clear_bit(prefix_len, prefix_usage->b);
104 struct mlxsw_sp_fib_key {
105 unsigned char addr[sizeof(struct in6_addr)];
106 unsigned char prefix_len;
109 enum mlxsw_sp_fib_entry_type {
110 MLXSW_SP_FIB_ENTRY_TYPE_REMOTE,
111 MLXSW_SP_FIB_ENTRY_TYPE_LOCAL,
112 MLXSW_SP_FIB_ENTRY_TYPE_TRAP,
115 struct mlxsw_sp_fib_entry {
116 struct rhash_head ht_node;
117 struct mlxsw_sp_fib_key key;
118 enum mlxsw_sp_fib_entry_type type;
120 u16 rif; /* used for action local */
121 struct mlxsw_sp_vr *vr;
124 struct mlxsw_sp_fib {
125 struct rhashtable ht;
126 unsigned long prefix_ref_count[MLXSW_SP_PREFIX_COUNT];
127 struct mlxsw_sp_prefix_usage prefix_usage;
130 static const struct rhashtable_params mlxsw_sp_fib_ht_params = {
131 .key_offset = offsetof(struct mlxsw_sp_fib_entry, key),
132 .head_offset = offsetof(struct mlxsw_sp_fib_entry, ht_node),
133 .key_len = sizeof(struct mlxsw_sp_fib_key),
134 .automatic_shrinking = true,
137 static int mlxsw_sp_fib_entry_insert(struct mlxsw_sp_fib *fib,
138 struct mlxsw_sp_fib_entry *fib_entry)
140 unsigned char prefix_len = fib_entry->key.prefix_len;
143 err = rhashtable_insert_fast(&fib->ht, &fib_entry->ht_node,
144 mlxsw_sp_fib_ht_params);
147 if (fib->prefix_ref_count[prefix_len]++ == 0)
148 mlxsw_sp_prefix_usage_set(&fib->prefix_usage, prefix_len);
152 static void mlxsw_sp_fib_entry_remove(struct mlxsw_sp_fib *fib,
153 struct mlxsw_sp_fib_entry *fib_entry)
155 unsigned char prefix_len = fib_entry->key.prefix_len;
157 if (--fib->prefix_ref_count[prefix_len] == 0)
158 mlxsw_sp_prefix_usage_clear(&fib->prefix_usage, prefix_len);
159 rhashtable_remove_fast(&fib->ht, &fib_entry->ht_node,
160 mlxsw_sp_fib_ht_params);
163 static struct mlxsw_sp_fib_entry *
164 mlxsw_sp_fib_entry_create(struct mlxsw_sp_fib *fib, const void *addr,
165 size_t addr_len, unsigned char prefix_len)
167 struct mlxsw_sp_fib_entry *fib_entry;
169 fib_entry = kzalloc(sizeof(*fib_entry), GFP_KERNEL);
172 memcpy(fib_entry->key.addr, addr, addr_len);
173 fib_entry->key.prefix_len = prefix_len;
177 static void mlxsw_sp_fib_entry_destroy(struct mlxsw_sp_fib_entry *fib_entry)
182 static struct mlxsw_sp_fib_entry *
183 mlxsw_sp_fib_entry_lookup(struct mlxsw_sp_fib *fib, const void *addr,
184 size_t addr_len, unsigned char prefix_len)
186 struct mlxsw_sp_fib_key key = {{ 0 } };
188 memcpy(key.addr, addr, addr_len);
189 key.prefix_len = prefix_len;
190 return rhashtable_lookup_fast(&fib->ht, &key, mlxsw_sp_fib_ht_params);
193 static struct mlxsw_sp_fib *mlxsw_sp_fib_create(void)
195 struct mlxsw_sp_fib *fib;
198 fib = kzalloc(sizeof(*fib), GFP_KERNEL);
200 return ERR_PTR(-ENOMEM);
201 err = rhashtable_init(&fib->ht, &mlxsw_sp_fib_ht_params);
203 goto err_rhashtable_init;
211 static void mlxsw_sp_fib_destroy(struct mlxsw_sp_fib *fib)
213 rhashtable_destroy(&fib->ht);
217 static struct mlxsw_sp_lpm_tree *
218 mlxsw_sp_lpm_tree_find_unused(struct mlxsw_sp *mlxsw_sp, bool one_reserved)
220 static struct mlxsw_sp_lpm_tree *lpm_tree;
223 for (i = 0; i < MLXSW_SP_LPM_TREE_COUNT; i++) {
224 lpm_tree = &mlxsw_sp->router.lpm_trees[i];
225 if (lpm_tree->ref_count == 0) {
227 one_reserved = false;
235 static int mlxsw_sp_lpm_tree_alloc(struct mlxsw_sp *mlxsw_sp,
236 struct mlxsw_sp_lpm_tree *lpm_tree)
238 char ralta_pl[MLXSW_REG_RALTA_LEN];
240 mlxsw_reg_ralta_pack(ralta_pl, true, lpm_tree->proto, lpm_tree->id);
241 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
244 static int mlxsw_sp_lpm_tree_free(struct mlxsw_sp *mlxsw_sp,
245 struct mlxsw_sp_lpm_tree *lpm_tree)
247 char ralta_pl[MLXSW_REG_RALTA_LEN];
249 mlxsw_reg_ralta_pack(ralta_pl, false, lpm_tree->proto, lpm_tree->id);
250 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
254 mlxsw_sp_lpm_tree_left_struct_set(struct mlxsw_sp *mlxsw_sp,
255 struct mlxsw_sp_prefix_usage *prefix_usage,
256 struct mlxsw_sp_lpm_tree *lpm_tree)
258 char ralst_pl[MLXSW_REG_RALST_LEN];
261 u8 last_prefix = MLXSW_REG_RALST_BIN_NO_CHILD;
263 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage)
266 mlxsw_reg_ralst_pack(ralst_pl, root_bin, lpm_tree->id);
267 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) {
270 mlxsw_reg_ralst_bin_pack(ralst_pl, prefix, last_prefix,
271 MLXSW_REG_RALST_BIN_NO_CHILD);
272 last_prefix = prefix;
274 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
277 static struct mlxsw_sp_lpm_tree *
278 mlxsw_sp_lpm_tree_create(struct mlxsw_sp *mlxsw_sp,
279 struct mlxsw_sp_prefix_usage *prefix_usage,
280 enum mlxsw_sp_l3proto proto, bool one_reserved)
282 struct mlxsw_sp_lpm_tree *lpm_tree;
285 lpm_tree = mlxsw_sp_lpm_tree_find_unused(mlxsw_sp, one_reserved);
287 return ERR_PTR(-EBUSY);
288 lpm_tree->proto = proto;
289 err = mlxsw_sp_lpm_tree_alloc(mlxsw_sp, lpm_tree);
293 err = mlxsw_sp_lpm_tree_left_struct_set(mlxsw_sp, prefix_usage,
296 goto err_left_struct_set;
300 mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
304 static int mlxsw_sp_lpm_tree_destroy(struct mlxsw_sp *mlxsw_sp,
305 struct mlxsw_sp_lpm_tree *lpm_tree)
307 return mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
310 static struct mlxsw_sp_lpm_tree *
311 mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp,
312 struct mlxsw_sp_prefix_usage *prefix_usage,
313 enum mlxsw_sp_l3proto proto, bool one_reserved)
315 struct mlxsw_sp_lpm_tree *lpm_tree;
318 for (i = 0; i < MLXSW_SP_LPM_TREE_COUNT; i++) {
319 lpm_tree = &mlxsw_sp->router.lpm_trees[i];
320 if (lpm_tree->proto == proto &&
321 mlxsw_sp_prefix_usage_eq(&lpm_tree->prefix_usage,
325 lpm_tree = mlxsw_sp_lpm_tree_create(mlxsw_sp, prefix_usage,
326 proto, one_reserved);
327 if (IS_ERR(lpm_tree))
331 lpm_tree->ref_count++;
335 static int mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp,
336 struct mlxsw_sp_lpm_tree *lpm_tree)
338 if (--lpm_tree->ref_count == 0)
339 return mlxsw_sp_lpm_tree_destroy(mlxsw_sp, lpm_tree);
343 static void mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp)
345 struct mlxsw_sp_lpm_tree *lpm_tree;
348 for (i = 0; i < MLXSW_SP_LPM_TREE_COUNT; i++) {
349 lpm_tree = &mlxsw_sp->router.lpm_trees[i];
350 lpm_tree->id = i + MLXSW_SP_LPM_TREE_MIN;
354 static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp)
356 struct mlxsw_sp_vr *vr;
359 for (i = 0; i < MLXSW_SP_VIRTUAL_ROUTER_MAX; i++) {
360 vr = &mlxsw_sp->router.vrs[i];
367 static int mlxsw_sp_vr_lpm_tree_bind(struct mlxsw_sp *mlxsw_sp,
368 struct mlxsw_sp_vr *vr)
370 char raltb_pl[MLXSW_REG_RALTB_LEN];
372 mlxsw_reg_raltb_pack(raltb_pl, vr->id, vr->proto, vr->lpm_tree->id);
373 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
376 static int mlxsw_sp_vr_lpm_tree_unbind(struct mlxsw_sp *mlxsw_sp,
377 struct mlxsw_sp_vr *vr)
379 char raltb_pl[MLXSW_REG_RALTB_LEN];
381 /* Bind to tree 0 which is default */
382 mlxsw_reg_raltb_pack(raltb_pl, vr->id, vr->proto, 0);
383 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
386 static u32 mlxsw_sp_fix_tb_id(u32 tb_id)
388 /* For our purpose, squash main and local table into one */
389 if (tb_id == RT_TABLE_LOCAL)
390 tb_id = RT_TABLE_MAIN;
394 static struct mlxsw_sp_vr *mlxsw_sp_vr_find(struct mlxsw_sp *mlxsw_sp,
396 enum mlxsw_sp_l3proto proto)
398 struct mlxsw_sp_vr *vr;
401 tb_id = mlxsw_sp_fix_tb_id(tb_id);
402 for (i = 0; i < MLXSW_SP_VIRTUAL_ROUTER_MAX; i++) {
403 vr = &mlxsw_sp->router.vrs[i];
404 if (vr->used && vr->proto == proto && vr->tb_id == tb_id)
410 static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp,
411 unsigned char prefix_len,
413 enum mlxsw_sp_l3proto proto)
415 struct mlxsw_sp_prefix_usage req_prefix_usage;
416 struct mlxsw_sp_lpm_tree *lpm_tree;
417 struct mlxsw_sp_vr *vr;
420 vr = mlxsw_sp_vr_find_unused(mlxsw_sp);
422 return ERR_PTR(-EBUSY);
423 vr->fib = mlxsw_sp_fib_create();
425 return ERR_CAST(vr->fib);
429 mlxsw_sp_prefix_usage_zero(&req_prefix_usage);
430 mlxsw_sp_prefix_usage_set(&req_prefix_usage, prefix_len);
431 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
433 if (IS_ERR(lpm_tree)) {
434 err = PTR_ERR(lpm_tree);
437 vr->lpm_tree = lpm_tree;
438 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, vr);
446 mlxsw_sp_lpm_tree_put(mlxsw_sp, vr->lpm_tree);
448 mlxsw_sp_fib_destroy(vr->fib);
453 static void mlxsw_sp_vr_destroy(struct mlxsw_sp *mlxsw_sp,
454 struct mlxsw_sp_vr *vr)
456 mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, vr);
457 mlxsw_sp_lpm_tree_put(mlxsw_sp, vr->lpm_tree);
458 mlxsw_sp_fib_destroy(vr->fib);
463 mlxsw_sp_vr_lpm_tree_check(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr,
464 struct mlxsw_sp_prefix_usage *req_prefix_usage)
466 struct mlxsw_sp_lpm_tree *lpm_tree;
468 if (mlxsw_sp_prefix_usage_eq(req_prefix_usage,
469 &vr->lpm_tree->prefix_usage))
472 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, req_prefix_usage,
474 if (IS_ERR(lpm_tree)) {
475 /* We failed to get a tree according to the required
476 * prefix usage. However, the current tree might be still good
477 * for us if our requirement is subset of the prefixes used
480 if (mlxsw_sp_prefix_usage_subset(req_prefix_usage,
481 &vr->lpm_tree->prefix_usage))
483 return PTR_ERR(lpm_tree);
486 mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, vr);
487 mlxsw_sp_lpm_tree_put(mlxsw_sp, vr->lpm_tree);
488 vr->lpm_tree = lpm_tree;
489 return mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, vr);
492 static struct mlxsw_sp_vr *mlxsw_sp_vr_get(struct mlxsw_sp *mlxsw_sp,
493 unsigned char prefix_len,
495 enum mlxsw_sp_l3proto proto)
497 struct mlxsw_sp_vr *vr;
500 tb_id = mlxsw_sp_fix_tb_id(tb_id);
501 vr = mlxsw_sp_vr_find(mlxsw_sp, tb_id, proto);
503 vr = mlxsw_sp_vr_create(mlxsw_sp, prefix_len, tb_id, proto);
507 struct mlxsw_sp_prefix_usage req_prefix_usage;
509 mlxsw_sp_prefix_usage_cpy(&req_prefix_usage,
510 &vr->fib->prefix_usage);
511 mlxsw_sp_prefix_usage_set(&req_prefix_usage, prefix_len);
512 /* Need to replace LPM tree in case new prefix is required. */
513 err = mlxsw_sp_vr_lpm_tree_check(mlxsw_sp, vr,
521 static void mlxsw_sp_vr_put(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr)
523 /* Destroy virtual router entity in case the associated FIB is empty
524 * and allow it to be used for other tables in future. Otherwise,
525 * check if some prefix usage did not disappear and change tree if
526 * that is the case. Note that in case new, smaller tree cannot be
527 * allocated, the original one will be kept being used.
529 if (mlxsw_sp_prefix_usage_none(&vr->fib->prefix_usage))
530 mlxsw_sp_vr_destroy(mlxsw_sp, vr);
532 mlxsw_sp_vr_lpm_tree_check(mlxsw_sp, vr,
533 &vr->fib->prefix_usage);
536 static void mlxsw_sp_vrs_init(struct mlxsw_sp *mlxsw_sp)
538 struct mlxsw_sp_vr *vr;
541 for (i = 0; i < MLXSW_SP_VIRTUAL_ROUTER_MAX; i++) {
542 vr = &mlxsw_sp->router.vrs[i];
547 static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
549 char rgcr_pl[MLXSW_REG_RGCR_LEN];
551 mlxsw_reg_rgcr_pack(rgcr_pl, true);
552 mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, MLXSW_SP_RIF_MAX);
553 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
556 static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
558 char rgcr_pl[MLXSW_REG_RGCR_LEN];
560 mlxsw_reg_rgcr_pack(rgcr_pl, false);
561 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
564 int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
568 err = __mlxsw_sp_router_init(mlxsw_sp);
571 mlxsw_sp_lpm_init(mlxsw_sp);
572 mlxsw_sp_vrs_init(mlxsw_sp);
576 void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
578 __mlxsw_sp_router_fini(mlxsw_sp);
581 static int mlxsw_sp_fib_entry_op4_local(struct mlxsw_sp *mlxsw_sp,
582 struct mlxsw_sp_fib_entry *fib_entry,
583 enum mlxsw_reg_ralue_op op)
585 char ralue_pl[MLXSW_REG_RALUE_LEN];
586 u32 *p_dip = (u32 *) fib_entry->key.addr;
587 struct mlxsw_sp_vr *vr = fib_entry->vr;
589 mlxsw_reg_ralue_pack4(ralue_pl, vr->proto, op, vr->id,
590 fib_entry->key.prefix_len, *p_dip);
591 mlxsw_reg_ralue_act_local_pack(ralue_pl,
592 MLXSW_REG_RALUE_TRAP_ACTION_NOP, 0,
594 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
597 static int mlxsw_sp_fib_entry_op4_trap(struct mlxsw_sp *mlxsw_sp,
598 struct mlxsw_sp_fib_entry *fib_entry,
599 enum mlxsw_reg_ralue_op op)
601 char ralue_pl[MLXSW_REG_RALUE_LEN];
602 u32 *p_dip = (u32 *) fib_entry->key.addr;
603 struct mlxsw_sp_vr *vr = fib_entry->vr;
605 mlxsw_reg_ralue_pack4(ralue_pl, vr->proto, op, vr->id,
606 fib_entry->key.prefix_len, *p_dip);
607 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
608 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
611 static int mlxsw_sp_fib_entry_op4(struct mlxsw_sp *mlxsw_sp,
612 struct mlxsw_sp_fib_entry *fib_entry,
613 enum mlxsw_reg_ralue_op op)
615 switch (fib_entry->type) {
616 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
618 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
619 return mlxsw_sp_fib_entry_op4_local(mlxsw_sp, fib_entry, op);
620 case MLXSW_SP_FIB_ENTRY_TYPE_TRAP:
621 return mlxsw_sp_fib_entry_op4_trap(mlxsw_sp, fib_entry, op);
626 static int mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
627 struct mlxsw_sp_fib_entry *fib_entry,
628 enum mlxsw_reg_ralue_op op)
630 switch (fib_entry->vr->proto) {
631 case MLXSW_SP_L3_PROTO_IPV4:
632 return mlxsw_sp_fib_entry_op4(mlxsw_sp, fib_entry, op);
633 case MLXSW_SP_L3_PROTO_IPV6:
639 static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
640 struct mlxsw_sp_fib_entry *fib_entry)
642 enum mlxsw_reg_ralue_op op;
644 op = !fib_entry->added ? MLXSW_REG_RALUE_OP_WRITE_WRITE :
645 MLXSW_REG_RALUE_OP_WRITE_UPDATE;
646 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry, op);
649 static int mlxsw_sp_fib_entry_del(struct mlxsw_sp *mlxsw_sp,
650 struct mlxsw_sp_fib_entry *fib_entry)
652 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
653 MLXSW_REG_RALUE_OP_WRITE_DELETE);
656 struct mlxsw_sp_router_fib4_add_info {
657 struct switchdev_trans_item tritem;
658 struct mlxsw_sp *mlxsw_sp;
659 struct mlxsw_sp_fib_entry *fib_entry;
662 static void mlxsw_sp_router_fib4_add_info_destroy(void const *data)
664 const struct mlxsw_sp_router_fib4_add_info *info = data;
665 struct mlxsw_sp_fib_entry *fib_entry = info->fib_entry;
666 struct mlxsw_sp *mlxsw_sp = info->mlxsw_sp;
668 mlxsw_sp_fib_entry_destroy(fib_entry);
669 mlxsw_sp_vr_put(mlxsw_sp, fib_entry->vr);
674 mlxsw_sp_router_fib4_entry_init(struct mlxsw_sp *mlxsw_sp,
675 const struct switchdev_obj_ipv4_fib *fib4,
676 struct mlxsw_sp_fib_entry *fib_entry)
678 struct fib_info *fi = fib4->fi;
680 if (fib4->type == RTN_LOCAL || fib4->type == RTN_BROADCAST) {
681 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
684 if (fib4->type != RTN_UNICAST)
687 if (fi->fib_scope != RT_SCOPE_UNIVERSE) {
688 struct mlxsw_sp_rif *r;
690 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
691 r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, fi->fib_dev);
694 fib_entry->rif = r->rif;
701 mlxsw_sp_router_fib4_add_prepare(struct mlxsw_sp_port *mlxsw_sp_port,
702 const struct switchdev_obj_ipv4_fib *fib4,
703 struct switchdev_trans *trans)
705 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
706 struct mlxsw_sp_router_fib4_add_info *info;
707 struct mlxsw_sp_fib_entry *fib_entry;
708 struct mlxsw_sp_vr *vr;
711 vr = mlxsw_sp_vr_get(mlxsw_sp, fib4->dst_len, fib4->tb_id,
712 MLXSW_SP_L3_PROTO_IPV4);
716 fib_entry = mlxsw_sp_fib_entry_create(vr->fib, &fib4->dst,
717 sizeof(fib4->dst), fib4->dst_len);
720 goto err_fib_entry_create;
724 err = mlxsw_sp_router_fib4_entry_init(mlxsw_sp, fib4, fib_entry);
726 goto err_fib4_entry_init;
728 info = kmalloc(sizeof(*info), GFP_KERNEL);
733 info->mlxsw_sp = mlxsw_sp;
734 info->fib_entry = fib_entry;
735 switchdev_trans_item_enqueue(trans, info,
736 mlxsw_sp_router_fib4_add_info_destroy,
742 mlxsw_sp_fib_entry_destroy(fib_entry);
743 err_fib_entry_create:
744 mlxsw_sp_vr_put(mlxsw_sp, vr);
749 mlxsw_sp_router_fib4_add_commit(struct mlxsw_sp_port *mlxsw_sp_port,
750 const struct switchdev_obj_ipv4_fib *fib4,
751 struct switchdev_trans *trans)
753 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
754 struct mlxsw_sp_router_fib4_add_info *info;
755 struct mlxsw_sp_fib_entry *fib_entry;
756 struct mlxsw_sp_vr *vr;
759 info = switchdev_trans_item_dequeue(trans);
760 fib_entry = info->fib_entry;
764 err = mlxsw_sp_fib_entry_insert(fib_entry->vr->fib, fib_entry);
766 goto err_fib_entry_insert;
767 err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
769 goto err_fib_entry_add;
773 mlxsw_sp_fib_entry_remove(vr->fib, fib_entry);
774 err_fib_entry_insert:
775 mlxsw_sp_fib_entry_destroy(fib_entry);
776 mlxsw_sp_vr_put(mlxsw_sp, vr);
780 int mlxsw_sp_router_fib4_add(struct mlxsw_sp_port *mlxsw_sp_port,
781 const struct switchdev_obj_ipv4_fib *fib4,
782 struct switchdev_trans *trans)
784 if (switchdev_trans_ph_prepare(trans))
785 return mlxsw_sp_router_fib4_add_prepare(mlxsw_sp_port,
787 return mlxsw_sp_router_fib4_add_commit(mlxsw_sp_port,
791 int mlxsw_sp_router_fib4_del(struct mlxsw_sp_port *mlxsw_sp_port,
792 const struct switchdev_obj_ipv4_fib *fib4)
794 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
795 struct mlxsw_sp_fib_entry *fib_entry;
796 struct mlxsw_sp_vr *vr;
798 vr = mlxsw_sp_vr_find(mlxsw_sp, fib4->tb_id, MLXSW_SP_L3_PROTO_IPV4);
800 dev_warn(mlxsw_sp->bus_info->dev, "Failed to find virtual router for FIB4 entry being removed.\n");
803 fib_entry = mlxsw_sp_fib_entry_lookup(vr->fib, &fib4->dst,
804 sizeof(fib4->dst), fib4->dst_len);
806 dev_warn(mlxsw_sp->bus_info->dev, "Failed to find FIB4 entry being removed.\n");
809 mlxsw_sp_fib_entry_del(mlxsw_sp_port->mlxsw_sp, fib_entry);
810 mlxsw_sp_fib_entry_remove(vr->fib, fib_entry);
811 mlxsw_sp_fib_entry_destroy(fib_entry);
812 mlxsw_sp_vr_put(mlxsw_sp, vr);