mlxsw: spectrum_router: Use correct tree index for binding
[cascardo/linux.git] / drivers / net / ethernet / mellanox / mlxsw / spectrum_router.c
index 3f5c51d..1b3a2cb 100644 (file)
@@ -43,6 +43,7 @@
 #include <net/netevent.h>
 #include <net/neighbour.h>
 #include <net/arp.h>
+#include <net/ip_fib.h>
 
 #include "spectrum.h"
 #include "core.h"
@@ -122,17 +123,20 @@ struct mlxsw_sp_nexthop_group;
 
 struct mlxsw_sp_fib_entry {
        struct rhash_head ht_node;
+       struct list_head list;
        struct mlxsw_sp_fib_key key;
        enum mlxsw_sp_fib_entry_type type;
        unsigned int ref_count;
        u16 rif; /* used for action local */
        struct mlxsw_sp_vr *vr;
+       struct fib_info *fi;
        struct list_head nexthop_group_node;
        struct mlxsw_sp_nexthop_group *nh_group;
 };
 
 struct mlxsw_sp_fib {
        struct rhashtable ht;
+       struct list_head entry_list;
        unsigned long prefix_ref_count[MLXSW_SP_PREFIX_COUNT];
        struct mlxsw_sp_prefix_usage prefix_usage;
 };
@@ -154,6 +158,7 @@ static int mlxsw_sp_fib_entry_insert(struct mlxsw_sp_fib *fib,
                                     mlxsw_sp_fib_ht_params);
        if (err)
                return err;
+       list_add_tail(&fib_entry->list, &fib->entry_list);
        if (fib->prefix_ref_count[prefix_len]++ == 0)
                mlxsw_sp_prefix_usage_set(&fib->prefix_usage, prefix_len);
        return 0;
@@ -166,6 +171,7 @@ static void mlxsw_sp_fib_entry_remove(struct mlxsw_sp_fib *fib,
 
        if (--fib->prefix_ref_count[prefix_len] == 0)
                mlxsw_sp_prefix_usage_clear(&fib->prefix_usage, prefix_len);
+       list_del(&fib_entry->list);
        rhashtable_remove_fast(&fib->ht, &fib_entry->ht_node,
                               mlxsw_sp_fib_ht_params);
 }
@@ -216,6 +222,7 @@ static struct mlxsw_sp_fib *mlxsw_sp_fib_create(void)
        err = rhashtable_init(&fib->ht, &mlxsw_sp_fib_ht_params);
        if (err)
                goto err_rhashtable_init;
+       INIT_LIST_HEAD(&fib->entry_list);
        return fib;
 
 err_rhashtable_init:
@@ -252,7 +259,9 @@ static int mlxsw_sp_lpm_tree_alloc(struct mlxsw_sp *mlxsw_sp,
 {
        char ralta_pl[MLXSW_REG_RALTA_LEN];
 
-       mlxsw_reg_ralta_pack(ralta_pl, true, lpm_tree->proto, lpm_tree->id);
+       mlxsw_reg_ralta_pack(ralta_pl, true,
+                            (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
+                            lpm_tree->id);
        return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
 }
 
@@ -261,7 +270,9 @@ static int mlxsw_sp_lpm_tree_free(struct mlxsw_sp *mlxsw_sp,
 {
        char ralta_pl[MLXSW_REG_RALTA_LEN];
 
-       mlxsw_reg_ralta_pack(ralta_pl, false, lpm_tree->proto, lpm_tree->id);
+       mlxsw_reg_ralta_pack(ralta_pl, false,
+                            (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
+                            lpm_tree->id);
        return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
 }
 
@@ -368,10 +379,12 @@ static void mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp)
 
 static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp)
 {
+       struct mlxsw_resources *resources;
        struct mlxsw_sp_vr *vr;
        int i;
 
-       for (i = 0; i < MLXSW_SP_VIRTUAL_ROUTER_MAX; i++) {
+       resources = mlxsw_core_resources_get(mlxsw_sp->core);
+       for (i = 0; i < resources->max_virtual_routers; i++) {
                vr = &mlxsw_sp->router.vrs[i];
                if (!vr->used)
                        return vr;
@@ -384,7 +397,9 @@ static int mlxsw_sp_vr_lpm_tree_bind(struct mlxsw_sp *mlxsw_sp,
 {
        char raltb_pl[MLXSW_REG_RALTB_LEN];
 
-       mlxsw_reg_raltb_pack(raltb_pl, vr->id, vr->proto, vr->lpm_tree->id);
+       mlxsw_reg_raltb_pack(raltb_pl, vr->id,
+                            (enum mlxsw_reg_ralxx_protocol) vr->proto,
+                            vr->lpm_tree->id);
        return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
 }
 
@@ -394,7 +409,8 @@ static int mlxsw_sp_vr_lpm_tree_unbind(struct mlxsw_sp *mlxsw_sp,
        char raltb_pl[MLXSW_REG_RALTB_LEN];
 
        /* Bind to tree 0 which is default */
-       mlxsw_reg_raltb_pack(raltb_pl, vr->id, vr->proto, 0);
+       mlxsw_reg_raltb_pack(raltb_pl, vr->id,
+                            (enum mlxsw_reg_ralxx_protocol) vr->proto, 0);
        return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
 }
 
@@ -410,11 +426,14 @@ static struct mlxsw_sp_vr *mlxsw_sp_vr_find(struct mlxsw_sp *mlxsw_sp,
                                            u32 tb_id,
                                            enum mlxsw_sp_l3proto proto)
 {
+       struct mlxsw_resources *resources;
        struct mlxsw_sp_vr *vr;
        int i;
 
        tb_id = mlxsw_sp_fix_tb_id(tb_id);
-       for (i = 0; i < MLXSW_SP_VIRTUAL_ROUTER_MAX; i++) {
+
+       resources = mlxsw_core_resources_get(mlxsw_sp->core);
+       for (i = 0; i < resources->max_virtual_routers; i++) {
                vr = &mlxsw_sp->router.vrs[i];
                if (vr->used && vr->proto == proto && vr->tb_id == tb_id)
                        return vr;
@@ -548,15 +567,33 @@ static void mlxsw_sp_vr_put(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr)
                                           &vr->fib->prefix_usage);
 }
 
-static void mlxsw_sp_vrs_init(struct mlxsw_sp *mlxsw_sp)
+static int mlxsw_sp_vrs_init(struct mlxsw_sp *mlxsw_sp)
 {
+       struct mlxsw_resources *resources;
        struct mlxsw_sp_vr *vr;
        int i;
 
-       for (i = 0; i < MLXSW_SP_VIRTUAL_ROUTER_MAX; i++) {
+       resources = mlxsw_core_resources_get(mlxsw_sp->core);
+       if (!resources->max_virtual_routers_valid)
+               return -EIO;
+
+       mlxsw_sp->router.vrs = kcalloc(resources->max_virtual_routers,
+                                      sizeof(struct mlxsw_sp_vr),
+                                      GFP_KERNEL);
+       if (!mlxsw_sp->router.vrs)
+               return -ENOMEM;
+
+       for (i = 0; i < resources->max_virtual_routers; i++) {
                vr = &mlxsw_sp->router.vrs[i];
                vr->id = i;
        }
+
+       return 0;
+}
+
+static void mlxsw_sp_vrs_fini(struct mlxsw_sp *mlxsw_sp)
+{
+       kfree(mlxsw_sp->router.vrs);
 }
 
 struct mlxsw_sp_neigh_key {
@@ -1081,9 +1118,10 @@ static int mlxsw_sp_adj_index_mass_update_vr(struct mlxsw_sp *mlxsw_sp,
 {
        char raleu_pl[MLXSW_REG_RALEU_LEN];
 
-       mlxsw_reg_raleu_pack(raleu_pl, vr->proto, vr->id,
-                            adj_index, ecmp_size,
-                            new_adj_index, new_ecmp_size);
+       mlxsw_reg_raleu_pack(raleu_pl,
+                            (enum mlxsw_reg_ralxx_protocol) vr->proto, vr->id,
+                            adj_index, ecmp_size, new_adj_index,
+                            new_ecmp_size);
        return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raleu), raleu_pl);
 }
 
@@ -1489,50 +1527,6 @@ static void mlxsw_sp_nexthop_group_put(struct mlxsw_sp *mlxsw_sp,
        mlxsw_sp_nexthop_group_destroy(mlxsw_sp, nh_grp);
 }
 
-static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
-{
-       char rgcr_pl[MLXSW_REG_RGCR_LEN];
-
-       mlxsw_reg_rgcr_pack(rgcr_pl, true);
-       mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, MLXSW_SP_RIF_MAX);
-       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
-}
-
-static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
-{
-       char rgcr_pl[MLXSW_REG_RGCR_LEN];
-
-       mlxsw_reg_rgcr_pack(rgcr_pl, false);
-       mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
-}
-
-int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
-{
-       int err;
-
-       INIT_LIST_HEAD(&mlxsw_sp->router.nexthop_neighs_list);
-       INIT_LIST_HEAD(&mlxsw_sp->router.nexthop_group_list);
-       err = __mlxsw_sp_router_init(mlxsw_sp);
-       if (err)
-               return err;
-       mlxsw_sp_lpm_init(mlxsw_sp);
-       mlxsw_sp_vrs_init(mlxsw_sp);
-       err = mlxsw_sp_neigh_init(mlxsw_sp);
-       if (err)
-               goto err_neigh_init;
-       return 0;
-
-err_neigh_init:
-       __mlxsw_sp_router_fini(mlxsw_sp);
-       return err;
-}
-
-void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
-{
-       mlxsw_sp_neigh_fini(mlxsw_sp);
-       __mlxsw_sp_router_fini(mlxsw_sp);
-}
-
 static int mlxsw_sp_fib_entry_op4_remote(struct mlxsw_sp *mlxsw_sp,
                                         struct mlxsw_sp_fib_entry *fib_entry,
                                         enum mlxsw_reg_ralue_op op)
@@ -1558,8 +1552,9 @@ static int mlxsw_sp_fib_entry_op4_remote(struct mlxsw_sp *mlxsw_sp,
                trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
        }
 
-       mlxsw_reg_ralue_pack4(ralue_pl, vr->proto, op, vr->id,
-                             fib_entry->key.prefix_len, *p_dip);
+       mlxsw_reg_ralue_pack4(ralue_pl,
+                             (enum mlxsw_reg_ralxx_protocol) vr->proto, op,
+                             vr->id, fib_entry->key.prefix_len, *p_dip);
        mlxsw_reg_ralue_act_remote_pack(ralue_pl, trap_action, trap_id,
                                        adjacency_index, ecmp_size);
        return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
@@ -1573,8 +1568,9 @@ static int mlxsw_sp_fib_entry_op4_local(struct mlxsw_sp *mlxsw_sp,
        u32 *p_dip = (u32 *) fib_entry->key.addr;
        struct mlxsw_sp_vr *vr = fib_entry->vr;
 
-       mlxsw_reg_ralue_pack4(ralue_pl, vr->proto, op, vr->id,
-                             fib_entry->key.prefix_len, *p_dip);
+       mlxsw_reg_ralue_pack4(ralue_pl,
+                             (enum mlxsw_reg_ralxx_protocol) vr->proto, op,
+                             vr->id, fib_entry->key.prefix_len, *p_dip);
        mlxsw_reg_ralue_act_local_pack(ralue_pl,
                                       MLXSW_REG_RALUE_TRAP_ACTION_NOP, 0,
                                       fib_entry->rif);
@@ -1589,8 +1585,9 @@ static int mlxsw_sp_fib_entry_op4_trap(struct mlxsw_sp *mlxsw_sp,
        u32 *p_dip = (u32 *) fib_entry->key.addr;
        struct mlxsw_sp_vr *vr = fib_entry->vr;
 
-       mlxsw_reg_ralue_pack4(ralue_pl, vr->proto, op, vr->id,
-                             fib_entry->key.prefix_len, *p_dip);
+       mlxsw_reg_ralue_pack4(ralue_pl,
+                             (enum mlxsw_reg_ralxx_protocol) vr->proto, op,
+                             vr->id, fib_entry->key.prefix_len, *p_dip);
        mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
        return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
 }
@@ -1637,94 +1634,102 @@ static int mlxsw_sp_fib_entry_del(struct mlxsw_sp *mlxsw_sp,
                                     MLXSW_REG_RALUE_OP_WRITE_DELETE);
 }
 
-struct mlxsw_sp_router_fib4_add_info {
-       struct switchdev_trans_item tritem;
-       struct mlxsw_sp *mlxsw_sp;
-       struct mlxsw_sp_fib_entry *fib_entry;
-};
-
-static void mlxsw_sp_router_fib4_add_info_destroy(void const *data)
-{
-       const struct mlxsw_sp_router_fib4_add_info *info = data;
-       struct mlxsw_sp_fib_entry *fib_entry = info->fib_entry;
-       struct mlxsw_sp *mlxsw_sp = info->mlxsw_sp;
-       struct mlxsw_sp_vr *vr = fib_entry->vr;
-
-       mlxsw_sp_fib_entry_destroy(fib_entry);
-       mlxsw_sp_vr_put(mlxsw_sp, vr);
-       kfree(info);
-}
-
 static int
 mlxsw_sp_router_fib4_entry_init(struct mlxsw_sp *mlxsw_sp,
-                               const struct switchdev_obj_ipv4_fib *fib4,
+                               const struct fib_entry_notifier_info *fen_info,
                                struct mlxsw_sp_fib_entry *fib_entry)
 {
-       struct fib_info *fi = fib4->fi;
+       struct fib_info *fi = fen_info->fi;
+       struct mlxsw_sp_rif *r = NULL;
+       int nhsel;
+       int err;
 
-       if (fib4->type == RTN_LOCAL || fib4->type == RTN_BROADCAST) {
+       if (fen_info->type == RTN_LOCAL || fen_info->type == RTN_BROADCAST) {
                fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
                return 0;
        }
-       if (fib4->type != RTN_UNICAST)
+       if (fen_info->type != RTN_UNICAST)
                return -EINVAL;
 
-       if (fi->fib_scope != RT_SCOPE_UNIVERSE) {
-               struct mlxsw_sp_rif *r;
+       for (nhsel = 0; nhsel < fi->fib_nhs; nhsel++) {
+               const struct fib_nh *nh = &fi->fib_nh[nhsel];
+
+               if (!nh->nh_dev)
+                       continue;
+               r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, nh->nh_dev);
+               if (!r) {
+                       /* In case router interface is not found for
+                        * at least one of the nexthops, that means
+                        * the nexthop points to some device unrelated
+                        * to us. Set trap and pass the packets for
+                        * this prefix to kernel.
+                        */
+                       break;
+               }
+       }
 
+       if (!r) {
+               fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
+               return 0;
+       }
+
+       if (fi->fib_scope != RT_SCOPE_UNIVERSE) {
                fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
-               r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, fi->fib_dev);
-               if (!r)
-                       return -EINVAL;
                fib_entry->rif = r->rif;
-               return 0;
+       } else {
+               fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
+               err = mlxsw_sp_nexthop_group_get(mlxsw_sp, fib_entry, fi);
+               if (err)
+                       return err;
        }
-       fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
-       return mlxsw_sp_nexthop_group_get(mlxsw_sp, fib_entry, fi);
+       fib_info_offload_inc(fen_info->fi);
+       return 0;
 }
 
 static void
 mlxsw_sp_router_fib4_entry_fini(struct mlxsw_sp *mlxsw_sp,
                                struct mlxsw_sp_fib_entry *fib_entry)
 {
-       if (fib_entry->type != MLXSW_SP_FIB_ENTRY_TYPE_REMOTE)
-               return;
-       mlxsw_sp_nexthop_group_put(mlxsw_sp, fib_entry);
+       if (fib_entry->type != MLXSW_SP_FIB_ENTRY_TYPE_TRAP)
+               fib_info_offload_dec(fib_entry->fi);
+       if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_REMOTE)
+               mlxsw_sp_nexthop_group_put(mlxsw_sp, fib_entry);
 }
 
 static struct mlxsw_sp_fib_entry *
 mlxsw_sp_fib_entry_get(struct mlxsw_sp *mlxsw_sp,
-                      const struct switchdev_obj_ipv4_fib *fib4)
+                      const struct fib_entry_notifier_info *fen_info)
 {
        struct mlxsw_sp_fib_entry *fib_entry;
-       struct fib_info *fi = fib4->fi;
+       struct fib_info *fi = fen_info->fi;
        struct mlxsw_sp_vr *vr;
        int err;
 
-       vr = mlxsw_sp_vr_get(mlxsw_sp, fib4->dst_len, fib4->tb_id,
+       vr = mlxsw_sp_vr_get(mlxsw_sp, fen_info->dst_len, fen_info->tb_id,
                             MLXSW_SP_L3_PROTO_IPV4);
        if (IS_ERR(vr))
                return ERR_CAST(vr);
 
-       fib_entry = mlxsw_sp_fib_entry_lookup(vr->fib, &fib4->dst,
-                                             sizeof(fib4->dst),
-                                             fib4->dst_len, fi->fib_dev);
+       fib_entry = mlxsw_sp_fib_entry_lookup(vr->fib, &fen_info->dst,
+                                             sizeof(fen_info->dst),
+                                             fen_info->dst_len, fi->fib_dev);
        if (fib_entry) {
                /* Already exists, just take a reference */
                fib_entry->ref_count++;
                return fib_entry;
        }
-       fib_entry = mlxsw_sp_fib_entry_create(vr->fib, &fib4->dst,
-                                             sizeof(fib4->dst),
-                                             fib4->dst_len, fi->fib_dev);
+       fib_entry = mlxsw_sp_fib_entry_create(vr->fib, &fen_info->dst,
+                                             sizeof(fen_info->dst),
+                                             fen_info->dst_len, fi->fib_dev);
        if (!fib_entry) {
                err = -ENOMEM;
                goto err_fib_entry_create;
        }
        fib_entry->vr = vr;
+       fib_entry->fi = fi;
        fib_entry->ref_count = 1;
 
-       err = mlxsw_sp_router_fib4_entry_init(mlxsw_sp, fib4, fib_entry);
+       err = mlxsw_sp_router_fib4_entry_init(mlxsw_sp, fen_info, fib_entry);
        if (err)
                goto err_fib4_entry_init;
 
@@ -1740,21 +1745,23 @@ err_fib_entry_create:
 
 static struct mlxsw_sp_fib_entry *
 mlxsw_sp_fib_entry_find(struct mlxsw_sp *mlxsw_sp,
-                       const struct switchdev_obj_ipv4_fib *fib4)
+                       const struct fib_entry_notifier_info *fen_info)
 {
        struct mlxsw_sp_vr *vr;
 
-       vr = mlxsw_sp_vr_find(mlxsw_sp, fib4->tb_id, MLXSW_SP_L3_PROTO_IPV4);
+       vr = mlxsw_sp_vr_find(mlxsw_sp, fen_info->tb_id,
+                             MLXSW_SP_L3_PROTO_IPV4);
        if (!vr)
                return NULL;
 
-       return mlxsw_sp_fib_entry_lookup(vr->fib, &fib4->dst,
-                                        sizeof(fib4->dst), fib4->dst_len,
-                                        fib4->fi->fib_dev);
+       return mlxsw_sp_fib_entry_lookup(vr->fib, &fen_info->dst,
+                                        sizeof(fen_info->dst),
+                                        fen_info->dst_len,
+                                        fen_info->fi->fib_dev);
 }
 
-void mlxsw_sp_fib_entry_put(struct mlxsw_sp *mlxsw_sp,
-                           struct mlxsw_sp_fib_entry *fib_entry)
+static void mlxsw_sp_fib_entry_put(struct mlxsw_sp *mlxsw_sp,
+                                  struct mlxsw_sp_fib_entry *fib_entry)
 {
        struct mlxsw_sp_vr *vr = fib_entry->vr;
 
@@ -1765,60 +1772,43 @@ void mlxsw_sp_fib_entry_put(struct mlxsw_sp *mlxsw_sp,
        mlxsw_sp_vr_put(mlxsw_sp, vr);
 }
 
-static int
-mlxsw_sp_router_fib4_add_prepare(struct mlxsw_sp_port *mlxsw_sp_port,
-                                const struct switchdev_obj_ipv4_fib *fib4,
-                                struct switchdev_trans *trans)
+static void mlxsw_sp_fib_entry_put_all(struct mlxsw_sp *mlxsw_sp,
+                                      struct mlxsw_sp_fib_entry *fib_entry)
 {
-       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
-       struct mlxsw_sp_router_fib4_add_info *info;
-       struct mlxsw_sp_fib_entry *fib_entry;
-       int err;
+       unsigned int last_ref_count;
 
-       fib_entry = mlxsw_sp_fib_entry_get(mlxsw_sp, fib4);
-       if (IS_ERR(fib_entry))
-               return PTR_ERR(fib_entry);
-
-       info = kmalloc(sizeof(*info), GFP_KERNEL);
-       if (!info) {
-               err = -ENOMEM;
-               goto err_alloc_info;
-       }
-       info->mlxsw_sp = mlxsw_sp;
-       info->fib_entry = fib_entry;
-       switchdev_trans_item_enqueue(trans, info,
-                                    mlxsw_sp_router_fib4_add_info_destroy,
-                                    &info->tritem);
-       return 0;
-
-err_alloc_info:
-       mlxsw_sp_fib_entry_put(mlxsw_sp, fib_entry);
-       return err;
+       do {
+               last_ref_count = fib_entry->ref_count;
+               mlxsw_sp_fib_entry_put(mlxsw_sp, fib_entry);
+       } while (last_ref_count != 1);
 }
 
-static int
-mlxsw_sp_router_fib4_add_commit(struct mlxsw_sp_port *mlxsw_sp_port,
-                               const struct switchdev_obj_ipv4_fib *fib4,
-                               struct switchdev_trans *trans)
+static int mlxsw_sp_router_fib4_add(struct mlxsw_sp *mlxsw_sp,
+                                   struct fib_entry_notifier_info *fen_info)
 {
-       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
-       struct mlxsw_sp_router_fib4_add_info *info;
        struct mlxsw_sp_fib_entry *fib_entry;
        struct mlxsw_sp_vr *vr;
        int err;
 
-       info = switchdev_trans_item_dequeue(trans);
-       fib_entry = info->fib_entry;
-       kfree(info);
+       if (mlxsw_sp->router.aborted)
+               return 0;
+
+       fib_entry = mlxsw_sp_fib_entry_get(mlxsw_sp, fen_info);
+       if (IS_ERR(fib_entry)) {
+               dev_warn(mlxsw_sp->bus_info->dev, "Failed to get FIB4 entry being added.\n");
+               return PTR_ERR(fib_entry);
+       }
 
        if (fib_entry->ref_count != 1)
                return 0;
 
        vr = fib_entry->vr;
        err = mlxsw_sp_fib_entry_insert(vr->fib, fib_entry);
-       if (err)
+       if (err) {
+               dev_warn(mlxsw_sp->bus_info->dev, "Failed to insert FIB4 entry being added.\n");
                goto err_fib_entry_insert;
-       err = mlxsw_sp_fib_entry_update(mlxsw_sp_port->mlxsw_sp, fib_entry);
+       }
+       err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
        if (err)
                goto err_fib_entry_add;
        return 0;
@@ -1830,24 +1820,15 @@ err_fib_entry_insert:
        return err;
 }
 
-int mlxsw_sp_router_fib4_add(struct mlxsw_sp_port *mlxsw_sp_port,
-                            const struct switchdev_obj_ipv4_fib *fib4,
-                            struct switchdev_trans *trans)
-{
-       if (switchdev_trans_ph_prepare(trans))
-               return mlxsw_sp_router_fib4_add_prepare(mlxsw_sp_port,
-                                                       fib4, trans);
-       return mlxsw_sp_router_fib4_add_commit(mlxsw_sp_port,
-                                              fib4, trans);
-}
-
-int mlxsw_sp_router_fib4_del(struct mlxsw_sp_port *mlxsw_sp_port,
-                            const struct switchdev_obj_ipv4_fib *fib4)
+static int mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp,
+                                   struct fib_entry_notifier_info *fen_info)
 {
-       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
        struct mlxsw_sp_fib_entry *fib_entry;
 
-       fib_entry = mlxsw_sp_fib_entry_find(mlxsw_sp, fib4);
+       if (mlxsw_sp->router.aborted)
+               return 0;
+
+       fib_entry = mlxsw_sp_fib_entry_find(mlxsw_sp, fen_info);
        if (!fib_entry) {
                dev_warn(mlxsw_sp->bus_info->dev, "Failed to find FIB4 entry being removed.\n");
                return -ENOENT;
@@ -1861,3 +1842,173 @@ int mlxsw_sp_router_fib4_del(struct mlxsw_sp_port *mlxsw_sp_port,
        mlxsw_sp_fib_entry_put(mlxsw_sp, fib_entry);
        return 0;
 }
+
+static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp)
+{
+       char ralta_pl[MLXSW_REG_RALTA_LEN];
+       char ralst_pl[MLXSW_REG_RALST_LEN];
+       char raltb_pl[MLXSW_REG_RALTB_LEN];
+       char ralue_pl[MLXSW_REG_RALUE_LEN];
+       int err;
+
+       mlxsw_reg_ralta_pack(ralta_pl, true, MLXSW_REG_RALXX_PROTOCOL_IPV4,
+                            MLXSW_SP_LPM_TREE_MIN);
+       err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
+       if (err)
+               return err;
+
+       mlxsw_reg_ralst_pack(ralst_pl, 0xff, MLXSW_SP_LPM_TREE_MIN);
+       err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
+       if (err)
+               return err;
+
+       mlxsw_reg_raltb_pack(raltb_pl, 0, MLXSW_REG_RALXX_PROTOCOL_IPV4,
+                            MLXSW_SP_LPM_TREE_MIN);
+       err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
+       if (err)
+               return err;
+
+       mlxsw_reg_ralue_pack4(ralue_pl, MLXSW_SP_L3_PROTO_IPV4,
+                             MLXSW_REG_RALUE_OP_WRITE_WRITE, 0, 0, 0);
+       mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
+       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
+}
+
+static void mlxsw_sp_router_fib4_abort(struct mlxsw_sp *mlxsw_sp)
+{
+       struct mlxsw_resources *resources;
+       struct mlxsw_sp_fib_entry *fib_entry;
+       struct mlxsw_sp_fib_entry *tmp;
+       struct mlxsw_sp_vr *vr;
+       int i;
+       int err;
+
+       resources = mlxsw_core_resources_get(mlxsw_sp->core);
+       for (i = 0; i < resources->max_virtual_routers; i++) {
+               vr = &mlxsw_sp->router.vrs[i];
+               if (!vr->used)
+                       continue;
+
+               list_for_each_entry_safe(fib_entry, tmp,
+                                        &vr->fib->entry_list, list) {
+                       bool do_break = &tmp->list == &vr->fib->entry_list;
+
+                       mlxsw_sp_fib_entry_del(mlxsw_sp, fib_entry);
+                       mlxsw_sp_fib_entry_remove(fib_entry->vr->fib,
+                                                 fib_entry);
+                       mlxsw_sp_fib_entry_put_all(mlxsw_sp, fib_entry);
+                       if (do_break)
+                               break;
+               }
+       }
+       mlxsw_sp->router.aborted = true;
+       err = mlxsw_sp_router_set_abort_trap(mlxsw_sp);
+       if (err)
+               dev_warn(mlxsw_sp->bus_info->dev, "Failed to set abort trap.\n");
+}
+
+static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
+{
+       struct mlxsw_resources *resources;
+       char rgcr_pl[MLXSW_REG_RGCR_LEN];
+       int err;
+
+       resources = mlxsw_core_resources_get(mlxsw_sp->core);
+       if (!resources->max_rif_valid)
+               return -EIO;
+
+       mlxsw_sp->rifs = kcalloc(resources->max_rif,
+                                sizeof(struct mlxsw_sp_rif *), GFP_KERNEL);
+       if (!mlxsw_sp->rifs)
+               return -ENOMEM;
+
+       mlxsw_reg_rgcr_pack(rgcr_pl, true);
+       mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, resources->max_rif);
+       err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
+       if (err)
+               goto err_rgcr_fail;
+
+       return 0;
+
+err_rgcr_fail:
+       kfree(mlxsw_sp->rifs);
+       return err;
+}
+
+static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
+{
+       struct mlxsw_resources *resources;
+       char rgcr_pl[MLXSW_REG_RGCR_LEN];
+       int i;
+
+       mlxsw_reg_rgcr_pack(rgcr_pl, false);
+       mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
+
+       resources = mlxsw_core_resources_get(mlxsw_sp->core);
+       for (i = 0; i < resources->max_rif; i++)
+               WARN_ON_ONCE(mlxsw_sp->rifs[i]);
+
+       kfree(mlxsw_sp->rifs);
+}
+
+static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
+                                    unsigned long event, void *ptr)
+{
+       struct mlxsw_sp *mlxsw_sp = container_of(nb, struct mlxsw_sp, fib_nb);
+       struct fib_entry_notifier_info *fen_info = ptr;
+       int err;
+
+       switch (event) {
+       case FIB_EVENT_ENTRY_ADD:
+               err = mlxsw_sp_router_fib4_add(mlxsw_sp, fen_info);
+               if (err)
+                       mlxsw_sp_router_fib4_abort(mlxsw_sp);
+               break;
+       case FIB_EVENT_ENTRY_DEL:
+               mlxsw_sp_router_fib4_del(mlxsw_sp, fen_info);
+               break;
+       case FIB_EVENT_RULE_ADD: /* fall through */
+       case FIB_EVENT_RULE_DEL:
+               mlxsw_sp_router_fib4_abort(mlxsw_sp);
+               break;
+       }
+       return NOTIFY_DONE;
+}
+
+int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
+{
+       int err;
+
+       INIT_LIST_HEAD(&mlxsw_sp->router.nexthop_neighs_list);
+       INIT_LIST_HEAD(&mlxsw_sp->router.nexthop_group_list);
+       err = __mlxsw_sp_router_init(mlxsw_sp);
+       if (err)
+               return err;
+
+       mlxsw_sp_lpm_init(mlxsw_sp);
+       err = mlxsw_sp_vrs_init(mlxsw_sp);
+       if (err)
+               goto err_vrs_init;
+
+       err =  mlxsw_sp_neigh_init(mlxsw_sp);
+       if (err)
+               goto err_neigh_init;
+
+       mlxsw_sp->fib_nb.notifier_call = mlxsw_sp_router_fib_event;
+       register_fib_notifier(&mlxsw_sp->fib_nb);
+       return 0;
+
+err_neigh_init:
+       mlxsw_sp_vrs_fini(mlxsw_sp);
+err_vrs_init:
+       __mlxsw_sp_router_fini(mlxsw_sp);
+       return err;
+}
+
+void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
+{
+       unregister_fib_notifier(&mlxsw_sp->fib_nb);
+       mlxsw_sp_neigh_fini(mlxsw_sp);
+       mlxsw_sp_vrs_fini(mlxsw_sp);
+       __mlxsw_sp_router_fini(mlxsw_sp);
+}