]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
mlxsw: spectrum_router: Fix rif counter freeing routine
[karo-tx-linux.git] / drivers / net / ethernet / mellanox / mlxsw / spectrum_router.c
index bd8de6b9be718f967ca6967a06c00be21d2e3b6c..9f89c4137d2137f78bcda8f79a918b8db61a5189 100644 (file)
 #include <linux/in6.h>
 #include <linux/notifier.h>
 #include <linux/inetdevice.h>
+#include <linux/netdevice.h>
 #include <net/netevent.h>
 #include <net/neighbour.h>
 #include <net/arp.h>
 #include <net/ip_fib.h>
+#include <net/fib_rules.h>
+#include <net/l3mdev.h>
 
 #include "spectrum.h"
 #include "core.h"
 #include "reg.h"
+#include "spectrum_cnt.h"
+#include "spectrum_dpipe.h"
+#include "spectrum_router.h"
+
+struct mlxsw_sp_rif {
+       struct list_head nexthop_list;
+       struct list_head neigh_list;
+       struct net_device *dev;
+       struct mlxsw_sp_fid *f;
+       unsigned char addr[ETH_ALEN];
+       int mtu;
+       u16 rif_index;
+       u16 vr_id;
+       unsigned int counter_ingress;
+       bool counter_ingress_valid;
+       unsigned int counter_egress;
+       bool counter_egress_valid;
+};
+
+static unsigned int *
+mlxsw_sp_rif_p_counter_get(struct mlxsw_sp_rif *rif,
+                          enum mlxsw_sp_rif_counter_dir dir)
+{
+       switch (dir) {
+       case MLXSW_SP_RIF_COUNTER_EGRESS:
+               return &rif->counter_egress;
+       case MLXSW_SP_RIF_COUNTER_INGRESS:
+               return &rif->counter_ingress;
+       }
+       return NULL;
+}
+
+static bool
+mlxsw_sp_rif_counter_valid_get(struct mlxsw_sp_rif *rif,
+                              enum mlxsw_sp_rif_counter_dir dir)
+{
+       switch (dir) {
+       case MLXSW_SP_RIF_COUNTER_EGRESS:
+               return rif->counter_egress_valid;
+       case MLXSW_SP_RIF_COUNTER_INGRESS:
+               return rif->counter_ingress_valid;
+       }
+       return false;
+}
+
+static void
+mlxsw_sp_rif_counter_valid_set(struct mlxsw_sp_rif *rif,
+                              enum mlxsw_sp_rif_counter_dir dir,
+                              bool valid)
+{
+       switch (dir) {
+       case MLXSW_SP_RIF_COUNTER_EGRESS:
+               rif->counter_egress_valid = valid;
+               break;
+       case MLXSW_SP_RIF_COUNTER_INGRESS:
+               rif->counter_ingress_valid = valid;
+               break;
+       }
+}
+
+static int mlxsw_sp_rif_counter_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
+                                    unsigned int counter_index, bool enable,
+                                    enum mlxsw_sp_rif_counter_dir dir)
+{
+       char ritr_pl[MLXSW_REG_RITR_LEN];
+       bool is_egress = false;
+       int err;
+
+       if (dir == MLXSW_SP_RIF_COUNTER_EGRESS)
+               is_egress = true;
+       mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
+       err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
+       if (err)
+               return err;
+
+       mlxsw_reg_ritr_counter_pack(ritr_pl, counter_index, enable,
+                                   is_egress);
+       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
+}
+
+int mlxsw_sp_rif_counter_value_get(struct mlxsw_sp *mlxsw_sp,
+                                  struct mlxsw_sp_rif *rif,
+                                  enum mlxsw_sp_rif_counter_dir dir, u64 *cnt)
+{
+       char ricnt_pl[MLXSW_REG_RICNT_LEN];
+       unsigned int *p_counter_index;
+       bool valid;
+       int err;
+
+       valid = mlxsw_sp_rif_counter_valid_get(rif, dir);
+       if (!valid)
+               return -EINVAL;
+
+       p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
+       if (!p_counter_index)
+               return -EINVAL;
+       mlxsw_reg_ricnt_pack(ricnt_pl, *p_counter_index,
+                            MLXSW_REG_RICNT_OPCODE_NOP);
+       err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
+       if (err)
+               return err;
+       *cnt = mlxsw_reg_ricnt_good_unicast_packets_get(ricnt_pl);
+       return 0;
+}
+
+static int mlxsw_sp_rif_counter_clear(struct mlxsw_sp *mlxsw_sp,
+                                     unsigned int counter_index)
+{
+       char ricnt_pl[MLXSW_REG_RICNT_LEN];
+
+       mlxsw_reg_ricnt_pack(ricnt_pl, counter_index,
+                            MLXSW_REG_RICNT_OPCODE_CLEAR);
+       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
+}
+
+int mlxsw_sp_rif_counter_alloc(struct mlxsw_sp *mlxsw_sp,
+                              struct mlxsw_sp_rif *rif,
+                              enum mlxsw_sp_rif_counter_dir dir)
+{
+       unsigned int *p_counter_index;
+       int err;
+
+       p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
+       if (!p_counter_index)
+               return -EINVAL;
+       err = mlxsw_sp_counter_alloc(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
+                                    p_counter_index);
+       if (err)
+               return err;
+
+       err = mlxsw_sp_rif_counter_clear(mlxsw_sp, *p_counter_index);
+       if (err)
+               goto err_counter_clear;
+
+       err = mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
+                                       *p_counter_index, true, dir);
+       if (err)
+               goto err_counter_edit;
+       mlxsw_sp_rif_counter_valid_set(rif, dir, true);
+       return 0;
+
+err_counter_edit:
+err_counter_clear:
+       mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
+                             *p_counter_index);
+       return err;
+}
+
+void mlxsw_sp_rif_counter_free(struct mlxsw_sp *mlxsw_sp,
+                              struct mlxsw_sp_rif *rif,
+                              enum mlxsw_sp_rif_counter_dir dir)
+{
+       unsigned int *p_counter_index;
+
+       if (!mlxsw_sp_rif_counter_valid_get(rif, dir))
+               return;
+
+       p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir);
+       if (WARN_ON(!p_counter_index))
+               return;
+       mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
+                                 *p_counter_index, false, dir);
+       mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
+                             *p_counter_index);
+       mlxsw_sp_rif_counter_valid_set(rif, dir, false);
+}
+
+static struct mlxsw_sp_rif *
+mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
+                        const struct net_device *dev);
 
 #define mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) \
        for_each_set_bit(prefix, (prefix_usage)->b, MLXSW_SP_PREFIX_COUNT)
@@ -88,12 +261,6 @@ mlxsw_sp_prefix_usage_cpy(struct mlxsw_sp_prefix_usage *prefix_usage1,
        memcpy(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
 }
 
-static void
-mlxsw_sp_prefix_usage_zero(struct mlxsw_sp_prefix_usage *prefix_usage)
-{
-       memset(prefix_usage, 0, sizeof(*prefix_usage));
-}
-
 static void
 mlxsw_sp_prefix_usage_set(struct mlxsw_sp_prefix_usage *prefix_usage,
                          unsigned char prefix_len)
@@ -125,7 +292,7 @@ struct mlxsw_sp_fib_node {
        struct list_head entry_list;
        struct list_head list;
        struct rhash_head ht_node;
-       struct mlxsw_sp_vr *vr;
+       struct mlxsw_sp_fib *fib;
        struct mlxsw_sp_fib_key key;
 };
 
@@ -149,13 +316,17 @@ struct mlxsw_sp_fib_entry {
 struct mlxsw_sp_fib {
        struct rhashtable ht;
        struct list_head node_list;
+       struct mlxsw_sp_vr *vr;
+       struct mlxsw_sp_lpm_tree *lpm_tree;
        unsigned long prefix_ref_count[MLXSW_SP_PREFIX_COUNT];
        struct mlxsw_sp_prefix_usage prefix_usage;
+       enum mlxsw_sp_l3proto proto;
 };
 
 static const struct rhashtable_params mlxsw_sp_fib_ht_params;
 
-static struct mlxsw_sp_fib *mlxsw_sp_fib_create(void)
+static struct mlxsw_sp_fib *mlxsw_sp_fib_create(struct mlxsw_sp_vr *vr,
+                                               enum mlxsw_sp_l3proto proto)
 {
        struct mlxsw_sp_fib *fib;
        int err;
@@ -167,6 +338,8 @@ static struct mlxsw_sp_fib *mlxsw_sp_fib_create(void)
        if (err)
                goto err_rhashtable_init;
        INIT_LIST_HEAD(&fib->node_list);
+       fib->proto = proto;
+       fib->vr = vr;
        return fib;
 
 err_rhashtable_init:
@@ -177,24 +350,21 @@ err_rhashtable_init:
 static void mlxsw_sp_fib_destroy(struct mlxsw_sp_fib *fib)
 {
        WARN_ON(!list_empty(&fib->node_list));
+       WARN_ON(fib->lpm_tree);
        rhashtable_destroy(&fib->ht);
        kfree(fib);
 }
 
 static struct mlxsw_sp_lpm_tree *
-mlxsw_sp_lpm_tree_find_unused(struct mlxsw_sp *mlxsw_sp, bool one_reserved)
+mlxsw_sp_lpm_tree_find_unused(struct mlxsw_sp *mlxsw_sp)
 {
        static struct mlxsw_sp_lpm_tree *lpm_tree;
        int i;
 
-       for (i = 0; i < MLXSW_SP_LPM_TREE_COUNT; i++) {
-               lpm_tree = &mlxsw_sp->router.lpm_trees[i];
-               if (lpm_tree->ref_count == 0) {
-                       if (one_reserved)
-                               one_reserved = false;
-                       else
-                               return lpm_tree;
-               }
+       for (i = 0; i < mlxsw_sp->router.lpm.tree_count; i++) {
+               lpm_tree = &mlxsw_sp->router.lpm.trees[i];
+               if (lpm_tree->ref_count == 0)
+                       return lpm_tree;
        }
        return NULL;
 }
@@ -248,12 +418,12 @@ mlxsw_sp_lpm_tree_left_struct_set(struct mlxsw_sp *mlxsw_sp,
 static struct mlxsw_sp_lpm_tree *
 mlxsw_sp_lpm_tree_create(struct mlxsw_sp *mlxsw_sp,
                         struct mlxsw_sp_prefix_usage *prefix_usage,
-                        enum mlxsw_sp_l3proto proto, bool one_reserved)
+                        enum mlxsw_sp_l3proto proto)
 {
        struct mlxsw_sp_lpm_tree *lpm_tree;
        int err;
 
-       lpm_tree = mlxsw_sp_lpm_tree_find_unused(mlxsw_sp, one_reserved);
+       lpm_tree = mlxsw_sp_lpm_tree_find_unused(mlxsw_sp);
        if (!lpm_tree)
                return ERR_PTR(-EBUSY);
        lpm_tree->proto = proto;
@@ -283,13 +453,13 @@ static int mlxsw_sp_lpm_tree_destroy(struct mlxsw_sp *mlxsw_sp,
 static struct mlxsw_sp_lpm_tree *
 mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp,
                      struct mlxsw_sp_prefix_usage *prefix_usage,
-                     enum mlxsw_sp_l3proto proto, bool one_reserved)
+                     enum mlxsw_sp_l3proto proto)
 {
        struct mlxsw_sp_lpm_tree *lpm_tree;
        int i;
 
-       for (i = 0; i < MLXSW_SP_LPM_TREE_COUNT; i++) {
-               lpm_tree = &mlxsw_sp->router.lpm_trees[i];
+       for (i = 0; i < mlxsw_sp->router.lpm.tree_count; i++) {
+               lpm_tree = &mlxsw_sp->router.lpm.trees[i];
                if (lpm_tree->ref_count != 0 &&
                    lpm_tree->proto == proto &&
                    mlxsw_sp_prefix_usage_eq(&lpm_tree->prefix_usage,
@@ -297,7 +467,7 @@ mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp,
                        goto inc_ref_count;
        }
        lpm_tree = mlxsw_sp_lpm_tree_create(mlxsw_sp, prefix_usage,
-                                           proto, one_reserved);
+                                           proto);
        if (IS_ERR(lpm_tree))
                return lpm_tree;
 
@@ -314,15 +484,41 @@ static int mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp,
        return 0;
 }
 
-static void mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp)
+#define MLXSW_SP_LPM_TREE_MIN 2 /* trees 0 and 1 are reserved */
+
+static int mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp)
 {
        struct mlxsw_sp_lpm_tree *lpm_tree;
+       u64 max_trees;
        int i;
 
-       for (i = 0; i < MLXSW_SP_LPM_TREE_COUNT; i++) {
-               lpm_tree = &mlxsw_sp->router.lpm_trees[i];
+       if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_LPM_TREES))
+               return -EIO;
+
+       max_trees = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_LPM_TREES);
+       mlxsw_sp->router.lpm.tree_count = max_trees - MLXSW_SP_LPM_TREE_MIN;
+       mlxsw_sp->router.lpm.trees = kcalloc(mlxsw_sp->router.lpm.tree_count,
+                                            sizeof(struct mlxsw_sp_lpm_tree),
+                                            GFP_KERNEL);
+       if (!mlxsw_sp->router.lpm.trees)
+               return -ENOMEM;
+
+       for (i = 0; i < mlxsw_sp->router.lpm.tree_count; i++) {
+               lpm_tree = &mlxsw_sp->router.lpm.trees[i];
                lpm_tree->id = i + MLXSW_SP_LPM_TREE_MIN;
        }
+
+       return 0;
+}
+
+static void mlxsw_sp_lpm_fini(struct mlxsw_sp *mlxsw_sp)
+{
+       kfree(mlxsw_sp->router.lpm.trees);
+}
+
+static bool mlxsw_sp_vr_is_used(const struct mlxsw_sp_vr *vr)
+{
+       return !!vr->fib4;
 }
 
 static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp)
@@ -332,31 +528,31 @@ static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp)
 
        for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
                vr = &mlxsw_sp->router.vrs[i];
-               if (!vr->used)
+               if (!mlxsw_sp_vr_is_used(vr))
                        return vr;
        }
        return NULL;
 }
 
 static int mlxsw_sp_vr_lpm_tree_bind(struct mlxsw_sp *mlxsw_sp,
-                                    struct mlxsw_sp_vr *vr)
+                                    const struct mlxsw_sp_fib *fib)
 {
        char raltb_pl[MLXSW_REG_RALTB_LEN];
 
-       mlxsw_reg_raltb_pack(raltb_pl, vr->id,
-                            (enum mlxsw_reg_ralxx_protocol) vr->proto,
-                            vr->lpm_tree->id);
+       mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,
+                            (enum mlxsw_reg_ralxx_protocol) fib->proto,
+                            fib->lpm_tree->id);
        return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
 }
 
 static int mlxsw_sp_vr_lpm_tree_unbind(struct mlxsw_sp *mlxsw_sp,
-                                      struct mlxsw_sp_vr *vr)
+                                      const struct mlxsw_sp_fib *fib)
 {
        char raltb_pl[MLXSW_REG_RALTB_LEN];
 
        /* Bind to tree 0 which is default */
-       mlxsw_reg_raltb_pack(raltb_pl, vr->id,
-                            (enum mlxsw_reg_ralxx_protocol) vr->proto, 0);
+       mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,
+                            (enum mlxsw_reg_ralxx_protocol) fib->proto, 0);
        return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
 }
 
@@ -369,8 +565,7 @@ static u32 mlxsw_sp_fix_tb_id(u32 tb_id)
 }
 
 static struct mlxsw_sp_vr *mlxsw_sp_vr_find(struct mlxsw_sp *mlxsw_sp,
-                                           u32 tb_id,
-                                           enum mlxsw_sp_l3proto proto)
+                                           u32 tb_id)
 {
        struct mlxsw_sp_vr *vr;
        int i;
@@ -379,69 +574,50 @@ static struct mlxsw_sp_vr *mlxsw_sp_vr_find(struct mlxsw_sp *mlxsw_sp,
 
        for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
                vr = &mlxsw_sp->router.vrs[i];
-               if (vr->used && vr->proto == proto && vr->tb_id == tb_id)
+               if (mlxsw_sp_vr_is_used(vr) && vr->tb_id == tb_id)
                        return vr;
        }
        return NULL;
 }
 
+static struct mlxsw_sp_fib *mlxsw_sp_vr_fib(const struct mlxsw_sp_vr *vr,
+                                           enum mlxsw_sp_l3proto proto)
+{
+       switch (proto) {
+       case MLXSW_SP_L3_PROTO_IPV4:
+               return vr->fib4;
+       case MLXSW_SP_L3_PROTO_IPV6:
+               BUG_ON(1);
+       }
+       return NULL;
+}
+
 static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp,
-                                             unsigned char prefix_len,
-                                             u32 tb_id,
-                                             enum mlxsw_sp_l3proto proto)
+                                             u32 tb_id)
 {
-       struct mlxsw_sp_prefix_usage req_prefix_usage;
-       struct mlxsw_sp_lpm_tree *lpm_tree;
        struct mlxsw_sp_vr *vr;
-       int err;
 
        vr = mlxsw_sp_vr_find_unused(mlxsw_sp);
        if (!vr)
                return ERR_PTR(-EBUSY);
-       vr->fib = mlxsw_sp_fib_create();
-       if (IS_ERR(vr->fib))
-               return ERR_CAST(vr->fib);
-
-       vr->proto = proto;
+       vr->fib4 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV4);
+       if (IS_ERR(vr->fib4))
+               return ERR_CAST(vr->fib4);
        vr->tb_id = tb_id;
-       mlxsw_sp_prefix_usage_zero(&req_prefix_usage);
-       mlxsw_sp_prefix_usage_set(&req_prefix_usage, prefix_len);
-       lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
-                                        proto, true);
-       if (IS_ERR(lpm_tree)) {
-               err = PTR_ERR(lpm_tree);
-               goto err_tree_get;
-       }
-       vr->lpm_tree = lpm_tree;
-       err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, vr);
-       if (err)
-               goto err_tree_bind;
-
-       vr->used = true;
        return vr;
-
-err_tree_bind:
-       mlxsw_sp_lpm_tree_put(mlxsw_sp, vr->lpm_tree);
-err_tree_get:
-       mlxsw_sp_fib_destroy(vr->fib);
-
-       return ERR_PTR(err);
 }
 
-static void mlxsw_sp_vr_destroy(struct mlxsw_sp *mlxsw_sp,
-                               struct mlxsw_sp_vr *vr)
+static void mlxsw_sp_vr_destroy(struct mlxsw_sp_vr *vr)
 {
-       mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, vr);
-       mlxsw_sp_lpm_tree_put(mlxsw_sp, vr->lpm_tree);
-       mlxsw_sp_fib_destroy(vr->fib);
-       vr->used = false;
+       mlxsw_sp_fib_destroy(vr->fib4);
+       vr->fib4 = NULL;
 }
 
 static int
-mlxsw_sp_vr_lpm_tree_check(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr,
+mlxsw_sp_vr_lpm_tree_check(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib *fib,
                           struct mlxsw_sp_prefix_usage *req_prefix_usage)
 {
-       struct mlxsw_sp_lpm_tree *lpm_tree = vr->lpm_tree;
+       struct mlxsw_sp_lpm_tree *lpm_tree = fib->lpm_tree;
        struct mlxsw_sp_lpm_tree *new_tree;
        int err;
 
@@ -449,7 +625,7 @@ mlxsw_sp_vr_lpm_tree_check(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr,
                return 0;
 
        new_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, req_prefix_usage,
-                                        vr->proto, false);
+                                        fib->proto);
        if (IS_ERR(new_tree)) {
                /* We failed to get a tree according to the required
                 * prefix usage. However, the current tree might be still good
@@ -463,8 +639,8 @@ mlxsw_sp_vr_lpm_tree_check(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr,
        }
 
        /* Prevent packet loss by overwriting existing binding */
-       vr->lpm_tree = new_tree;
-       err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, vr);
+       fib->lpm_tree = new_tree;
+       err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib);
        if (err)
                goto err_tree_bind;
        mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree);
@@ -472,53 +648,26 @@ mlxsw_sp_vr_lpm_tree_check(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr,
        return 0;
 
 err_tree_bind:
-       vr->lpm_tree = lpm_tree;
+       fib->lpm_tree = lpm_tree;
        mlxsw_sp_lpm_tree_put(mlxsw_sp, new_tree);
        return err;
 }
 
-static struct mlxsw_sp_vr *mlxsw_sp_vr_get(struct mlxsw_sp *mlxsw_sp,
-                                          unsigned char prefix_len,
-                                          u32 tb_id,
-                                          enum mlxsw_sp_l3proto proto)
+static struct mlxsw_sp_vr *mlxsw_sp_vr_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id)
 {
        struct mlxsw_sp_vr *vr;
-       int err;
 
        tb_id = mlxsw_sp_fix_tb_id(tb_id);
-       vr = mlxsw_sp_vr_find(mlxsw_sp, tb_id, proto);
-       if (!vr) {
-               vr = mlxsw_sp_vr_create(mlxsw_sp, prefix_len, tb_id, proto);
-               if (IS_ERR(vr))
-                       return vr;
-       } else {
-               struct mlxsw_sp_prefix_usage req_prefix_usage;
-
-               mlxsw_sp_prefix_usage_cpy(&req_prefix_usage,
-                                         &vr->fib->prefix_usage);
-               mlxsw_sp_prefix_usage_set(&req_prefix_usage, prefix_len);
-               /* Need to replace LPM tree in case new prefix is required. */
-               err = mlxsw_sp_vr_lpm_tree_check(mlxsw_sp, vr,
-                                                &req_prefix_usage);
-               if (err)
-                       return ERR_PTR(err);
-       }
+       vr = mlxsw_sp_vr_find(mlxsw_sp, tb_id);
+       if (!vr)
+               vr = mlxsw_sp_vr_create(mlxsw_sp, tb_id);
        return vr;
 }
 
-static void mlxsw_sp_vr_put(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr)
+static void mlxsw_sp_vr_put(struct mlxsw_sp_vr *vr)
 {
-       /* Destroy virtual router entity in case the associated FIB is empty
-        * and allow it to be used for other tables in future. Otherwise,
-        * check if some prefix usage did not disappear and change tree if
-        * that is the case. Note that in case new, smaller tree cannot be
-        * allocated, the original one will be kept being used.
-        */
-       if (mlxsw_sp_prefix_usage_none(&vr->fib->prefix_usage))
-               mlxsw_sp_vr_destroy(mlxsw_sp, vr);
-       else
-               mlxsw_sp_vr_lpm_tree_check(mlxsw_sp, vr,
-                                          &vr->fib->prefix_usage);
+       if (!vr->rif_count && list_empty(&vr->fib4->node_list))
+               mlxsw_sp_vr_destroy(vr);
 }
 
 static int mlxsw_sp_vrs_init(struct mlxsw_sp *mlxsw_sp)
@@ -627,14 +776,14 @@ static struct mlxsw_sp_neigh_entry *
 mlxsw_sp_neigh_entry_create(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
 {
        struct mlxsw_sp_neigh_entry *neigh_entry;
-       struct mlxsw_sp_rif *r;
+       struct mlxsw_sp_rif *rif;
        int err;
 
-       r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, n->dev);
-       if (!r)
+       rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, n->dev);
+       if (!rif)
                return ERR_PTR(-EINVAL);
 
-       neigh_entry = mlxsw_sp_neigh_entry_alloc(mlxsw_sp, n, r->rif);
+       neigh_entry = mlxsw_sp_neigh_entry_alloc(mlxsw_sp, n, rif->rif_index);
        if (!neigh_entry)
                return ERR_PTR(-ENOMEM);
 
@@ -642,7 +791,7 @@ mlxsw_sp_neigh_entry_create(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
        if (err)
                goto err_neigh_entry_insert;
 
-       list_add(&neigh_entry->rif_list_node, &r->neigh_list);
+       list_add(&neigh_entry->rif_list_node, &rif->neigh_list);
 
        return neigh_entry;
 
@@ -1050,22 +1199,22 @@ static void mlxsw_sp_neigh_fini(struct mlxsw_sp *mlxsw_sp)
 }
 
 static int mlxsw_sp_neigh_rif_flush(struct mlxsw_sp *mlxsw_sp,
-                                   const struct mlxsw_sp_rif *r)
+                                   const struct mlxsw_sp_rif *rif)
 {
        char rauht_pl[MLXSW_REG_RAUHT_LEN];
 
        mlxsw_reg_rauht_pack(rauht_pl, MLXSW_REG_RAUHT_OP_WRITE_DELETE_ALL,
-                            r->rif, r->addr);
+                            rif->rif_index, rif->addr);
        return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
 }
 
 static void mlxsw_sp_neigh_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
-                                        struct mlxsw_sp_rif *r)
+                                        struct mlxsw_sp_rif *rif)
 {
        struct mlxsw_sp_neigh_entry *neigh_entry, *tmp;
 
-       mlxsw_sp_neigh_rif_flush(mlxsw_sp, r);
-       list_for_each_entry_safe(neigh_entry, tmp, &r->neigh_list,
+       mlxsw_sp_neigh_rif_flush(mlxsw_sp, rif);
+       list_for_each_entry_safe(neigh_entry, tmp, &rif->neigh_list,
                                 rif_list_node)
                mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
 }
@@ -1082,7 +1231,7 @@ struct mlxsw_sp_nexthop {
                                                */
        struct rhash_head ht_node;
        struct mlxsw_sp_nexthop_key key;
-       struct mlxsw_sp_rif *r;
+       struct mlxsw_sp_rif *rif;
        u8 should_offload:1, /* set indicates this neigh is connected and
                              * should be put to KVD linear area of this group.
                              */
@@ -1109,7 +1258,7 @@ struct mlxsw_sp_nexthop_group {
        u16 ecmp_size;
        u16 count;
        struct mlxsw_sp_nexthop nexthops[0];
-#define nh_rif nexthops[0].r
+#define nh_rif nexthops[0].rif
 };
 
 static const struct rhashtable_params mlxsw_sp_nexthop_group_ht_params = {
@@ -1171,7 +1320,7 @@ mlxsw_sp_nexthop_lookup(struct mlxsw_sp *mlxsw_sp,
 }
 
 static int mlxsw_sp_adj_index_mass_update_vr(struct mlxsw_sp *mlxsw_sp,
-                                            struct mlxsw_sp_vr *vr,
+                                            const struct mlxsw_sp_fib *fib,
                                             u32 adj_index, u16 ecmp_size,
                                             u32 new_adj_index,
                                             u16 new_ecmp_size)
@@ -1179,8 +1328,8 @@ 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,
-                            (enum mlxsw_reg_ralxx_protocol) vr->proto, vr->id,
-                            adj_index, ecmp_size, new_adj_index,
+                            (enum mlxsw_reg_ralxx_protocol) fib->proto,
+                            fib->vr->id, adj_index, ecmp_size, new_adj_index,
                             new_ecmp_size);
        return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raleu), raleu_pl);
 }
@@ -1190,14 +1339,14 @@ static int mlxsw_sp_adj_index_mass_update(struct mlxsw_sp *mlxsw_sp,
                                          u32 old_adj_index, u16 old_ecmp_size)
 {
        struct mlxsw_sp_fib_entry *fib_entry;
-       struct mlxsw_sp_vr *vr = NULL;
+       struct mlxsw_sp_fib *fib = NULL;
        int err;
 
        list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
-               if (vr == fib_entry->fib_node->vr)
+               if (fib == fib_entry->fib_node->fib)
                        continue;
-               vr = fib_entry->fib_node->vr;
-               err = mlxsw_sp_adj_index_mass_update_vr(mlxsw_sp, vr,
+               fib = fib_entry->fib_node->fib;
+               err = mlxsw_sp_adj_index_mass_update_vr(mlxsw_sp, fib,
                                                        old_adj_index,
                                                        old_ecmp_size,
                                                        nh_grp->adj_index,
@@ -1280,7 +1429,6 @@ mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp,
        bool old_adj_index_valid;
        u32 old_adj_index;
        u16 old_ecmp_size;
-       int ret;
        int i;
        int err;
 
@@ -1318,15 +1466,14 @@ mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp,
                 */
                goto set_trap;
 
-       ret = mlxsw_sp_kvdl_alloc(mlxsw_sp, ecmp_size);
-       if (ret < 0) {
+       err = mlxsw_sp_kvdl_alloc(mlxsw_sp, ecmp_size, &adj_index);
+       if (err) {
                /* We ran out of KVD linear space, just set the
                 * trap and let everything flow through kernel.
                 */
                dev_warn(mlxsw_sp->bus_info->dev, "Failed to allocate KVD linear area for nexthop group.\n");
                goto set_trap;
        }
-       adj_index = ret;
        old_adj_index_valid = nh_grp->adj_index_valid;
        old_adj_index = nh_grp->adj_index;
        old_ecmp_size = nh_grp->ecmp_size;
@@ -1399,22 +1546,22 @@ mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
 }
 
 static void mlxsw_sp_nexthop_rif_init(struct mlxsw_sp_nexthop *nh,
-                                     struct mlxsw_sp_rif *r)
+                                     struct mlxsw_sp_rif *rif)
 {
-       if (nh->r)
+       if (nh->rif)
                return;
 
-       nh->r = r;
-       list_add(&nh->rif_list_node, &r->nexthop_list);
+       nh->rif = rif;
+       list_add(&nh->rif_list_node, &rif->nexthop_list);
 }
 
 static void mlxsw_sp_nexthop_rif_fini(struct mlxsw_sp_nexthop *nh)
 {
-       if (!nh->r)
+       if (!nh->rif)
                return;
 
        list_del(&nh->rif_list_node);
-       nh->r = NULL;
+       nh->rif = NULL;
 }
 
 static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp,
@@ -1505,7 +1652,7 @@ static int mlxsw_sp_nexthop_init(struct mlxsw_sp *mlxsw_sp,
 {
        struct net_device *dev = fib_nh->nh_dev;
        struct in_device *in_dev;
-       struct mlxsw_sp_rif *r;
+       struct mlxsw_sp_rif *rif;
        int err;
 
        nh->nh_grp = nh_grp;
@@ -1514,15 +1661,18 @@ static int mlxsw_sp_nexthop_init(struct mlxsw_sp *mlxsw_sp,
        if (err)
                return err;
 
+       if (!dev)
+               return 0;
+
        in_dev = __in_dev_get_rtnl(dev);
        if (in_dev && IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) &&
            fib_nh->nh_flags & RTNH_F_LINKDOWN)
                return 0;
 
-       r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
-       if (!r)
+       rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
+       if (!rif)
                return 0;
-       mlxsw_sp_nexthop_rif_init(nh, r);
+       mlxsw_sp_nexthop_rif_init(nh, rif);
 
        err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
        if (err)
@@ -1548,7 +1698,7 @@ static void mlxsw_sp_nexthop_event(struct mlxsw_sp *mlxsw_sp,
 {
        struct mlxsw_sp_nexthop_key key;
        struct mlxsw_sp_nexthop *nh;
-       struct mlxsw_sp_rif *r;
+       struct mlxsw_sp_rif *rif;
 
        if (mlxsw_sp->router.aborted)
                return;
@@ -1558,13 +1708,13 @@ static void mlxsw_sp_nexthop_event(struct mlxsw_sp *mlxsw_sp,
        if (WARN_ON_ONCE(!nh))
                return;
 
-       r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, fib_nh->nh_dev);
-       if (!r)
+       rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, fib_nh->nh_dev);
+       if (!rif)
                return;
 
        switch (event) {
        case FIB_EVENT_NH_ADD:
-               mlxsw_sp_nexthop_rif_init(nh, r);
+               mlxsw_sp_nexthop_rif_init(nh, rif);
                mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
                break;
        case FIB_EVENT_NH_DEL:
@@ -1577,11 +1727,11 @@ static void mlxsw_sp_nexthop_event(struct mlxsw_sp *mlxsw_sp,
 }
 
 static void mlxsw_sp_nexthop_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
-                                          struct mlxsw_sp_rif *r)
+                                          struct mlxsw_sp_rif *rif)
 {
        struct mlxsw_sp_nexthop *nh, *tmp;
 
-       list_for_each_entry_safe(nh, tmp, &r->nexthop_list, rif_list_node) {
+       list_for_each_entry_safe(nh, tmp, &rif->nexthop_list, rif_list_node) {
                mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
                mlxsw_sp_nexthop_rif_fini(nh);
                mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
@@ -1699,7 +1849,7 @@ static void mlxsw_sp_fib_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
 {
        fib_entry->offloaded = true;
 
-       switch (fib_entry->fib_node->vr->proto) {
+       switch (fib_entry->fib_node->fib->proto) {
        case MLXSW_SP_L3_PROTO_IPV4:
                fib_info_offload_inc(fib_entry->nh_group->key.fi);
                break;
@@ -1711,7 +1861,7 @@ static void mlxsw_sp_fib_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
 static void
 mlxsw_sp_fib_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
 {
-       switch (fib_entry->fib_node->vr->proto) {
+       switch (fib_entry->fib_node->fib->proto) {
        case MLXSW_SP_L3_PROTO_IPV4:
                fib_info_offload_dec(fib_entry->nh_group->key.fi);
                break;
@@ -1751,8 +1901,8 @@ static int mlxsw_sp_fib_entry_op4_remote(struct mlxsw_sp *mlxsw_sp,
                                         enum mlxsw_reg_ralue_op op)
 {
        char ralue_pl[MLXSW_REG_RALUE_LEN];
+       struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib;
        u32 *p_dip = (u32 *) fib_entry->fib_node->key.addr;
-       struct mlxsw_sp_vr *vr = fib_entry->fib_node->vr;
        enum mlxsw_reg_ralue_trap_action trap_action;
        u16 trap_id = 0;
        u32 adjacency_index = 0;
@@ -1772,8 +1922,8 @@ static int mlxsw_sp_fib_entry_op4_remote(struct mlxsw_sp *mlxsw_sp,
        }
 
        mlxsw_reg_ralue_pack4(ralue_pl,
-                             (enum mlxsw_reg_ralxx_protocol) vr->proto, op,
-                             vr->id, fib_entry->fib_node->key.prefix_len,
+                             (enum mlxsw_reg_ralxx_protocol) fib->proto, op,
+                             fib->vr->id, fib_entry->fib_node->key.prefix_len,
                              *p_dip);
        mlxsw_reg_ralue_act_remote_pack(ralue_pl, trap_action, trap_id,
                                        adjacency_index, ecmp_size);
@@ -1784,27 +1934,28 @@ static int mlxsw_sp_fib_entry_op4_local(struct mlxsw_sp *mlxsw_sp,
                                        struct mlxsw_sp_fib_entry *fib_entry,
                                        enum mlxsw_reg_ralue_op op)
 {
-       struct mlxsw_sp_rif *r = fib_entry->nh_group->nh_rif;
+       struct mlxsw_sp_rif *rif = fib_entry->nh_group->nh_rif;
+       struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib;
        enum mlxsw_reg_ralue_trap_action trap_action;
        char ralue_pl[MLXSW_REG_RALUE_LEN];
        u32 *p_dip = (u32 *) fib_entry->fib_node->key.addr;
-       struct mlxsw_sp_vr *vr = fib_entry->fib_node->vr;
        u16 trap_id = 0;
-       u16 rif = 0;
+       u16 rif_index = 0;
 
        if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
                trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
-               rif = r->rif;
+               rif_index = rif->rif_index;
        } else {
                trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
                trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
        }
 
        mlxsw_reg_ralue_pack4(ralue_pl,
-                             (enum mlxsw_reg_ralxx_protocol) vr->proto, op,
-                             vr->id, fib_entry->fib_node->key.prefix_len,
+                             (enum mlxsw_reg_ralxx_protocol) fib->proto, op,
+                             fib->vr->id, fib_entry->fib_node->key.prefix_len,
                              *p_dip);
-       mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, trap_id, rif);
+       mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, trap_id,
+                                      rif_index);
        return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
 }
 
@@ -1812,13 +1963,13 @@ static int mlxsw_sp_fib_entry_op4_trap(struct mlxsw_sp *mlxsw_sp,
                                       struct mlxsw_sp_fib_entry *fib_entry,
                                       enum mlxsw_reg_ralue_op op)
 {
+       struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib;
        char ralue_pl[MLXSW_REG_RALUE_LEN];
        u32 *p_dip = (u32 *) fib_entry->fib_node->key.addr;
-       struct mlxsw_sp_vr *vr = fib_entry->fib_node->vr;
 
        mlxsw_reg_ralue_pack4(ralue_pl,
-                             (enum mlxsw_reg_ralxx_protocol) vr->proto, op,
-                             vr->id, fib_entry->fib_node->key.prefix_len,
+                             (enum mlxsw_reg_ralxx_protocol) fib->proto, op,
+                             fib->vr->id, fib_entry->fib_node->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);
@@ -1845,7 +1996,7 @@ static int mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
 {
        int err = -EINVAL;
 
-       switch (fib_entry->fib_node->vr->proto) {
+       switch (fib_entry->fib_node->fib->proto) {
        case MLXSW_SP_L3_PROTO_IPV4:
                err = mlxsw_sp_fib_entry_op4(mlxsw_sp, fib_entry, op);
                break;
@@ -1877,17 +2028,29 @@ mlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp,
 {
        struct fib_info *fi = fen_info->fi;
 
-       if (fen_info->type == RTN_LOCAL || fen_info->type == RTN_BROADCAST) {
+       switch (fen_info->type) {
+       case RTN_BROADCAST: /* fall through */
+       case RTN_LOCAL:
                fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
                return 0;
-       }
-       if (fen_info->type != RTN_UNICAST)
-               return -EINVAL;
-       if (fi->fib_nh->nh_scope != RT_SCOPE_LINK)
+       case RTN_UNREACHABLE: /* fall through */
+       case RTN_BLACKHOLE: /* fall through */
+       case RTN_PROHIBIT:
+               /* Packets hitting these routes need to be trapped, but
+                * can do so with a lower priority than packets directed
+                * at the host, so use action type local instead of trap.
+                */
                fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
-       else
-               fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
-       return 0;
+               return 0;
+       case RTN_UNICAST:
+               if (fi->fib_nh->nh_scope != RT_SCOPE_LINK)
+                       fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
+               else
+                       fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
+               return 0;
+       default:
+               return -EINVAL;
+       }
 }
 
 static struct mlxsw_sp_fib_entry *
@@ -1996,7 +2159,7 @@ mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr,
 }
 
 static struct mlxsw_sp_fib_node *
-mlxsw_sp_fib_node_create(struct mlxsw_sp_vr *vr, const void *addr,
+mlxsw_sp_fib_node_create(struct mlxsw_sp_fib *fib, const void *addr,
                         size_t addr_len, unsigned char prefix_len)
 {
        struct mlxsw_sp_fib_node *fib_node;
@@ -2006,18 +2169,15 @@ mlxsw_sp_fib_node_create(struct mlxsw_sp_vr *vr, const void *addr,
                return NULL;
 
        INIT_LIST_HEAD(&fib_node->entry_list);
-       list_add(&fib_node->list, &vr->fib->node_list);
+       list_add(&fib_node->list, &fib->node_list);
        memcpy(fib_node->key.addr, addr, addr_len);
        fib_node->key.prefix_len = prefix_len;
-       mlxsw_sp_fib_node_insert(vr->fib, fib_node);
-       fib_node->vr = vr;
 
        return fib_node;
 }
 
 static void mlxsw_sp_fib_node_destroy(struct mlxsw_sp_fib_node *fib_node)
 {
-       mlxsw_sp_fib_node_remove(fib_node->vr->fib, fib_node);
        list_del(&fib_node->list);
        WARN_ON(!list_empty(&fib_node->entry_list));
        kfree(fib_node);
@@ -2034,7 +2194,7 @@ mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node,
 static void mlxsw_sp_fib_node_prefix_inc(struct mlxsw_sp_fib_node *fib_node)
 {
        unsigned char prefix_len = fib_node->key.prefix_len;
-       struct mlxsw_sp_fib *fib = fib_node->vr->fib;
+       struct mlxsw_sp_fib *fib = fib_node->fib;
 
        if (fib->prefix_ref_count[prefix_len]++ == 0)
                mlxsw_sp_prefix_usage_set(&fib->prefix_usage, prefix_len);
@@ -2043,32 +2203,98 @@ static void mlxsw_sp_fib_node_prefix_inc(struct mlxsw_sp_fib_node *fib_node)
 static void mlxsw_sp_fib_node_prefix_dec(struct mlxsw_sp_fib_node *fib_node)
 {
        unsigned char prefix_len = fib_node->key.prefix_len;
-       struct mlxsw_sp_fib *fib = fib_node->vr->fib;
+       struct mlxsw_sp_fib *fib = fib_node->fib;
 
        if (--fib->prefix_ref_count[prefix_len] == 0)
                mlxsw_sp_prefix_usage_clear(&fib->prefix_usage, prefix_len);
 }
 
+static int mlxsw_sp_fib_node_init(struct mlxsw_sp *mlxsw_sp,
+                                 struct mlxsw_sp_fib_node *fib_node,
+                                 struct mlxsw_sp_fib *fib)
+{
+       struct mlxsw_sp_prefix_usage req_prefix_usage;
+       struct mlxsw_sp_lpm_tree *lpm_tree;
+       int err;
+
+       err = mlxsw_sp_fib_node_insert(fib, fib_node);
+       if (err)
+               return err;
+       fib_node->fib = fib;
+
+       mlxsw_sp_prefix_usage_cpy(&req_prefix_usage, &fib->prefix_usage);
+       mlxsw_sp_prefix_usage_set(&req_prefix_usage, fib_node->key.prefix_len);
+
+       if (!mlxsw_sp_prefix_usage_none(&fib->prefix_usage)) {
+               err = mlxsw_sp_vr_lpm_tree_check(mlxsw_sp, fib,
+                                                &req_prefix_usage);
+               if (err)
+                       goto err_tree_check;
+       } else {
+               lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
+                                                fib->proto);
+               if (IS_ERR(lpm_tree))
+                       return PTR_ERR(lpm_tree);
+               fib->lpm_tree = lpm_tree;
+               err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib);
+               if (err)
+                       goto err_tree_bind;
+       }
+
+       mlxsw_sp_fib_node_prefix_inc(fib_node);
+
+       return 0;
+
+err_tree_bind:
+       fib->lpm_tree = NULL;
+       mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree);
+err_tree_check:
+       fib_node->fib = NULL;
+       mlxsw_sp_fib_node_remove(fib, fib_node);
+       return err;
+}
+
+static void mlxsw_sp_fib_node_fini(struct mlxsw_sp *mlxsw_sp,
+                                  struct mlxsw_sp_fib_node *fib_node)
+{
+       struct mlxsw_sp_lpm_tree *lpm_tree = fib_node->fib->lpm_tree;
+       struct mlxsw_sp_fib *fib = fib_node->fib;
+
+       mlxsw_sp_fib_node_prefix_dec(fib_node);
+
+       if (mlxsw_sp_prefix_usage_none(&fib->prefix_usage)) {
+               mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, fib);
+               fib->lpm_tree = NULL;
+               mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree);
+       } else {
+               mlxsw_sp_vr_lpm_tree_check(mlxsw_sp, fib, &fib->prefix_usage);
+       }
+
+       fib_node->fib = NULL;
+       mlxsw_sp_fib_node_remove(fib, fib_node);
+}
+
 static struct mlxsw_sp_fib_node *
 mlxsw_sp_fib4_node_get(struct mlxsw_sp *mlxsw_sp,
                       const struct fib_entry_notifier_info *fen_info)
 {
        struct mlxsw_sp_fib_node *fib_node;
+       struct mlxsw_sp_fib *fib;
        struct mlxsw_sp_vr *vr;
        int err;
 
-       vr = mlxsw_sp_vr_get(mlxsw_sp, fen_info->dst_len, fen_info->tb_id,
-                            MLXSW_SP_L3_PROTO_IPV4);
+       vr = mlxsw_sp_vr_get(mlxsw_sp, fen_info->tb_id);
        if (IS_ERR(vr))
                return ERR_CAST(vr);
+       fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV4);
 
-       fib_node = mlxsw_sp_fib_node_lookup(vr->fib, &fen_info->dst,
+       fib_node = mlxsw_sp_fib_node_lookup(fib, &fen_info->dst,
                                            sizeof(fen_info->dst),
                                            fen_info->dst_len);
        if (fib_node)
                return fib_node;
 
-       fib_node = mlxsw_sp_fib_node_create(vr, &fen_info->dst,
+       fib_node = mlxsw_sp_fib_node_create(fib, &fen_info->dst,
                                            sizeof(fen_info->dst),
                                            fen_info->dst_len);
        if (!fib_node) {
@@ -2076,22 +2302,29 @@ mlxsw_sp_fib4_node_get(struct mlxsw_sp *mlxsw_sp,
                goto err_fib_node_create;
        }
 
+       err = mlxsw_sp_fib_node_init(mlxsw_sp, fib_node, fib);
+       if (err)
+               goto err_fib_node_init;
+
        return fib_node;
 
+err_fib_node_init:
+       mlxsw_sp_fib_node_destroy(fib_node);
 err_fib_node_create:
-       mlxsw_sp_vr_put(mlxsw_sp, vr);
+       mlxsw_sp_vr_put(vr);
        return ERR_PTR(err);
 }
 
 static void mlxsw_sp_fib4_node_put(struct mlxsw_sp *mlxsw_sp,
                                   struct mlxsw_sp_fib_node *fib_node)
 {
-       struct mlxsw_sp_vr *vr = fib_node->vr;
+       struct mlxsw_sp_vr *vr = fib_node->fib->vr;
 
        if (!list_empty(&fib_node->entry_list))
                return;
+       mlxsw_sp_fib_node_fini(mlxsw_sp, fib_node);
        mlxsw_sp_fib_node_destroy(fib_node);
-       mlxsw_sp_vr_put(mlxsw_sp, vr);
+       mlxsw_sp_vr_put(vr);
 }
 
 static struct mlxsw_sp_fib_entry *
@@ -2236,8 +2469,6 @@ static int mlxsw_sp_fib4_node_entry_link(struct mlxsw_sp *mlxsw_sp,
        if (err)
                goto err_fib4_node_entry_add;
 
-       mlxsw_sp_fib_node_prefix_inc(fib_node);
-
        return 0;
 
 err_fib4_node_entry_add:
@@ -2251,7 +2482,6 @@ mlxsw_sp_fib4_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
 {
        struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
 
-       mlxsw_sp_fib_node_prefix_dec(fib_node);
        mlxsw_sp_fib4_node_entry_del(mlxsw_sp, fib_node, fib_entry);
        mlxsw_sp_fib4_node_list_remove(fib_entry);
 }
@@ -2340,9 +2570,7 @@ 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;
+       int i, err;
 
        mlxsw_reg_ralta_pack(ralta_pl, true, MLXSW_REG_RALXX_PROTOCOL_IPV4,
                             MLXSW_SP_LPM_TREE_MIN);
@@ -2355,16 +2583,33 @@ static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp)
        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;
+       for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
+               struct mlxsw_sp_vr *vr = &mlxsw_sp->router.vrs[i];
+               char raltb_pl[MLXSW_REG_RALTB_LEN];
+               char ralue_pl[MLXSW_REG_RALUE_LEN];
 
-       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);
+               if (!mlxsw_sp_vr_is_used(vr))
+                       continue;
+
+               mlxsw_reg_raltb_pack(raltb_pl, vr->id,
+                                    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, vr->id, 0,
+                                     0);
+               mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
+               err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue),
+                                     ralue_pl);
+               if (err)
+                       return err;
+       }
+
+       return 0;
 }
 
 static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp,
@@ -2390,7 +2635,7 @@ static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp,
 static void mlxsw_sp_fib_node_flush(struct mlxsw_sp *mlxsw_sp,
                                    struct mlxsw_sp_fib_node *fib_node)
 {
-       switch (fib_node->vr->proto) {
+       switch (fib_node->fib->proto) {
        case MLXSW_SP_L3_PROTO_IPV4:
                mlxsw_sp_fib4_node_flush(mlxsw_sp, fib_node);
                break;
@@ -2400,26 +2645,32 @@ static void mlxsw_sp_fib_node_flush(struct mlxsw_sp *mlxsw_sp,
        }
 }
 
-static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp)
+static void mlxsw_sp_vr_fib_flush(struct mlxsw_sp *mlxsw_sp,
+                                 struct mlxsw_sp_vr *vr,
+                                 enum mlxsw_sp_l3proto proto)
 {
+       struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto);
        struct mlxsw_sp_fib_node *fib_node, *tmp;
-       struct mlxsw_sp_vr *vr;
+
+       list_for_each_entry_safe(fib_node, tmp, &fib->node_list, list) {
+               bool do_break = &tmp->list == &fib->node_list;
+
+               mlxsw_sp_fib_node_flush(mlxsw_sp, fib_node);
+               if (do_break)
+                       break;
+       }
+}
+
+static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp)
+{
        int i;
 
        for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
-               vr = &mlxsw_sp->router.vrs[i];
+               struct mlxsw_sp_vr *vr = &mlxsw_sp->router.vrs[i];
 
-               if (!vr->used)
+               if (!mlxsw_sp_vr_is_used(vr))
                        continue;
-
-               list_for_each_entry_safe(fib_node, tmp, &vr->fib->node_list,
-                                        list) {
-                       bool do_break = &tmp->list == &vr->fib->node_list;
-
-                       mlxsw_sp_fib_node_flush(mlxsw_sp, fib_node);
-                       if (do_break)
-                               break;
-               }
+               mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV4);
        }
 }
 
@@ -2437,86 +2688,24 @@ static void mlxsw_sp_router_fib4_abort(struct mlxsw_sp *mlxsw_sp)
                dev_warn(mlxsw_sp->bus_info->dev, "Failed to set abort trap.\n");
 }
 
-static int mlxsw_sp_router_rif_disable(struct mlxsw_sp *mlxsw_sp, u16 rif)
-{
-       char ritr_pl[MLXSW_REG_RITR_LEN];
-       int err;
-
-       mlxsw_reg_ritr_rif_pack(ritr_pl, rif);
-       err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
-       if (WARN_ON_ONCE(err))
-               return err;
-
-       mlxsw_reg_ritr_enable_set(ritr_pl, false);
-       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
-}
-
-void mlxsw_sp_router_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
-                                  struct mlxsw_sp_rif *r)
-{
-       mlxsw_sp_router_rif_disable(mlxsw_sp, r->rif);
-       mlxsw_sp_nexthop_rif_gone_sync(mlxsw_sp, r);
-       mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, r);
-}
+struct mlxsw_sp_fib_event_work {
+       struct work_struct work;
+       union {
+               struct fib_entry_notifier_info fen_info;
+               struct fib_rule_notifier_info fr_info;
+               struct fib_nh_notifier_info fnh_info;
+       };
+       struct mlxsw_sp *mlxsw_sp;
+       unsigned long event;
+};
 
-static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
+static void mlxsw_sp_router_fib_event_work(struct work_struct *work)
 {
-       char rgcr_pl[MLXSW_REG_RGCR_LEN];
-       u64 max_rifs;
-       int err;
-
-       if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_RIFS))
-               return -EIO;
-
-       max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
-       mlxsw_sp->rifs = kcalloc(max_rifs, 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, max_rifs);
-       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)
-{
-       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);
-
-       for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
-               WARN_ON_ONCE(mlxsw_sp->rifs[i]);
-
-       kfree(mlxsw_sp->rifs);
-}
-
-struct mlxsw_sp_fib_event_work {
-       struct work_struct work;
-       union {
-               struct fib_entry_notifier_info fen_info;
-               struct fib_nh_notifier_info fnh_info;
-       };
-       struct mlxsw_sp *mlxsw_sp;
-       unsigned long event;
-};
-
-static void mlxsw_sp_router_fib_event_work(struct work_struct *work)
-{
-       struct mlxsw_sp_fib_event_work *fib_work =
-               container_of(work, struct mlxsw_sp_fib_event_work, work);
-       struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
-       bool replace, append;
+       struct mlxsw_sp_fib_event_work *fib_work =
+               container_of(work, struct mlxsw_sp_fib_event_work, work);
+       struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
+       struct fib_rule *rule;
+       bool replace, append;
        int err;
 
        /* Protect internal structures from changes */
@@ -2539,7 +2728,10 @@ static void mlxsw_sp_router_fib_event_work(struct work_struct *work)
                break;
        case FIB_EVENT_RULE_ADD: /* fall through */
        case FIB_EVENT_RULE_DEL:
-               mlxsw_sp_router_fib4_abort(mlxsw_sp);
+               rule = fib_work->fr_info.rule;
+               if (!fib4_rule_default(rule) && !rule->l3mdev)
+                       mlxsw_sp_router_fib4_abort(mlxsw_sp);
+               fib_rule_put(rule);
                break;
        case FIB_EVENT_NH_ADD: /* fall through */
        case FIB_EVENT_NH_DEL:
@@ -2582,6 +2774,11 @@ static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
                 */
                fib_info_hold(fib_work->fen_info.fi);
                break;
+       case FIB_EVENT_RULE_ADD: /* fall through */
+       case FIB_EVENT_RULE_DEL:
+               memcpy(&fib_work->fr_info, ptr, sizeof(fib_work->fr_info));
+               fib_rule_get(fib_work->fr_info.rule);
+               break;
        case FIB_EVENT_NH_ADD: /* fall through */
        case FIB_EVENT_NH_DEL:
                memcpy(&fib_work->fnh_info, ptr, sizeof(fib_work->fnh_info));
@@ -2594,6 +2791,707 @@ static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
        return NOTIFY_DONE;
 }
 
+static struct mlxsw_sp_rif *
+mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
+                        const struct net_device *dev)
+{
+       int i;
+
+       for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
+               if (mlxsw_sp->rifs[i] && mlxsw_sp->rifs[i]->dev == dev)
+                       return mlxsw_sp->rifs[i];
+
+       return NULL;
+}
+
+static int mlxsw_sp_router_rif_disable(struct mlxsw_sp *mlxsw_sp, u16 rif)
+{
+       char ritr_pl[MLXSW_REG_RITR_LEN];
+       int err;
+
+       mlxsw_reg_ritr_rif_pack(ritr_pl, rif);
+       err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
+       if (WARN_ON_ONCE(err))
+               return err;
+
+       mlxsw_reg_ritr_enable_set(ritr_pl, false);
+       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
+}
+
+static void mlxsw_sp_router_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
+                                         struct mlxsw_sp_rif *rif)
+{
+       mlxsw_sp_router_rif_disable(mlxsw_sp, rif->rif_index);
+       mlxsw_sp_nexthop_rif_gone_sync(mlxsw_sp, rif);
+       mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, rif);
+}
+
+static bool mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *rif,
+                                      const struct in_device *in_dev,
+                                      unsigned long event)
+{
+       switch (event) {
+       case NETDEV_UP:
+               if (!rif)
+                       return true;
+               return false;
+       case NETDEV_DOWN:
+               if (rif && !in_dev->ifa_list &&
+                   !netif_is_l3_slave(rif->dev))
+                       return true;
+               /* It is possible we already removed the RIF ourselves
+                * if it was assigned to a netdev that is now a bridge
+                * or LAG slave.
+                */
+               return false;
+       }
+
+       return false;
+}
+
+#define MLXSW_SP_INVALID_INDEX_RIF 0xffff
+static int mlxsw_sp_avail_rif_get(struct mlxsw_sp *mlxsw_sp)
+{
+       int i;
+
+       for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
+               if (!mlxsw_sp->rifs[i])
+                       return i;
+
+       return MLXSW_SP_INVALID_INDEX_RIF;
+}
+
+static void mlxsw_sp_vport_rif_sp_attr_get(struct mlxsw_sp_port *mlxsw_sp_vport,
+                                          bool *p_lagged, u16 *p_system_port)
+{
+       u8 local_port = mlxsw_sp_vport->local_port;
+
+       *p_lagged = mlxsw_sp_vport->lagged;
+       *p_system_port = *p_lagged ? mlxsw_sp_vport->lag_id : local_port;
+}
+
+static int mlxsw_sp_vport_rif_sp_op(struct mlxsw_sp_port *mlxsw_sp_vport,
+                                   u16 vr_id, struct net_device *l3_dev,
+                                   u16 rif_index, bool create)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp;
+       bool lagged = mlxsw_sp_vport->lagged;
+       char ritr_pl[MLXSW_REG_RITR_LEN];
+       u16 system_port;
+
+       mlxsw_reg_ritr_pack(ritr_pl, create, MLXSW_REG_RITR_SP_IF, rif_index,
+                           vr_id, l3_dev->mtu, l3_dev->dev_addr);
+
+       mlxsw_sp_vport_rif_sp_attr_get(mlxsw_sp_vport, &lagged, &system_port);
+       mlxsw_reg_ritr_sp_if_pack(ritr_pl, lagged, system_port,
+                                 mlxsw_sp_vport_vid_get(mlxsw_sp_vport));
+
+       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
+}
+
+static void mlxsw_sp_vport_rif_sp_leave(struct mlxsw_sp_port *mlxsw_sp_vport);
+
+static u16 mlxsw_sp_rif_sp_to_fid(u16 rif_index)
+{
+       return MLXSW_SP_RFID_BASE + rif_index;
+}
+
+static struct mlxsw_sp_fid *
+mlxsw_sp_rfid_alloc(u16 fid, struct net_device *l3_dev)
+{
+       struct mlxsw_sp_fid *f;
+
+       f = kzalloc(sizeof(*f), GFP_KERNEL);
+       if (!f)
+               return NULL;
+
+       f->leave = mlxsw_sp_vport_rif_sp_leave;
+       f->ref_count = 0;
+       f->dev = l3_dev;
+       f->fid = fid;
+
+       return f;
+}
+
+static struct mlxsw_sp_rif *
+mlxsw_sp_rif_alloc(u16 rif_index, u16 vr_id, struct net_device *l3_dev,
+                  struct mlxsw_sp_fid *f)
+{
+       struct mlxsw_sp_rif *rif;
+
+       rif = kzalloc(sizeof(*rif), GFP_KERNEL);
+       if (!rif)
+               return NULL;
+
+       INIT_LIST_HEAD(&rif->nexthop_list);
+       INIT_LIST_HEAD(&rif->neigh_list);
+       ether_addr_copy(rif->addr, l3_dev->dev_addr);
+       rif->mtu = l3_dev->mtu;
+       rif->vr_id = vr_id;
+       rif->dev = l3_dev;
+       rif->rif_index = rif_index;
+       rif->f = f;
+
+       return rif;
+}
+
+u16 mlxsw_sp_rif_index(const struct mlxsw_sp_rif *rif)
+{
+       return rif->rif_index;
+}
+
+int mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif)
+{
+       return rif->dev->ifindex;
+}
+
+static struct mlxsw_sp_rif *
+mlxsw_sp_vport_rif_sp_create(struct mlxsw_sp_port *mlxsw_sp_vport,
+                            struct net_device *l3_dev)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp;
+       u32 tb_id = l3mdev_fib_table(l3_dev);
+       struct mlxsw_sp_vr *vr;
+       struct mlxsw_sp_fid *f;
+       struct mlxsw_sp_rif *rif;
+       u16 fid, rif_index;
+       int err;
+
+       rif_index = mlxsw_sp_avail_rif_get(mlxsw_sp);
+       if (rif_index == MLXSW_SP_INVALID_INDEX_RIF)
+               return ERR_PTR(-ERANGE);
+
+       vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id ? : RT_TABLE_MAIN);
+       if (IS_ERR(vr))
+               return ERR_CAST(vr);
+
+       err = mlxsw_sp_vport_rif_sp_op(mlxsw_sp_vport, vr->id, l3_dev,
+                                      rif_index, true);
+       if (err)
+               goto err_vport_rif_sp_op;
+
+       fid = mlxsw_sp_rif_sp_to_fid(rif_index);
+       err = mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, fid, true);
+       if (err)
+               goto err_rif_fdb_op;
+
+       f = mlxsw_sp_rfid_alloc(fid, l3_dev);
+       if (!f) {
+               err = -ENOMEM;
+               goto err_rfid_alloc;
+       }
+
+       rif = mlxsw_sp_rif_alloc(rif_index, vr->id, l3_dev, f);
+       if (!rif) {
+               err = -ENOMEM;
+               goto err_rif_alloc;
+       }
+
+       if (devlink_dpipe_table_counter_enabled(priv_to_devlink(mlxsw_sp->core),
+                                               MLXSW_SP_DPIPE_TABLE_NAME_ERIF)) {
+               err = mlxsw_sp_rif_counter_alloc(mlxsw_sp, rif,
+                                                MLXSW_SP_RIF_COUNTER_EGRESS);
+               if (err)
+                       netdev_dbg(mlxsw_sp_vport->dev,
+                                  "Counter alloc Failed err=%d\n", err);
+       }
+
+       f->rif = rif;
+       mlxsw_sp->rifs[rif_index] = rif;
+       vr->rif_count++;
+
+       return rif;
+
+err_rif_alloc:
+       kfree(f);
+err_rfid_alloc:
+       mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, fid, false);
+err_rif_fdb_op:
+       mlxsw_sp_vport_rif_sp_op(mlxsw_sp_vport, vr->id, l3_dev, rif_index,
+                                false);
+err_vport_rif_sp_op:
+       mlxsw_sp_vr_put(vr);
+       return ERR_PTR(err);
+}
+
+static void mlxsw_sp_vport_rif_sp_destroy(struct mlxsw_sp_port *mlxsw_sp_vport,
+                                         struct mlxsw_sp_rif *rif)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp;
+       struct mlxsw_sp_vr *vr = &mlxsw_sp->router.vrs[rif->vr_id];
+       struct net_device *l3_dev = rif->dev;
+       struct mlxsw_sp_fid *f = rif->f;
+       u16 rif_index = rif->rif_index;
+       u16 fid = f->fid;
+
+       mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif);
+
+       mlxsw_sp_rif_counter_free(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS);
+       mlxsw_sp_rif_counter_free(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_INGRESS);
+
+       vr->rif_count--;
+       mlxsw_sp->rifs[rif_index] = NULL;
+       f->rif = NULL;
+
+       kfree(rif);
+
+       kfree(f);
+
+       mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, fid, false);
+
+       mlxsw_sp_vport_rif_sp_op(mlxsw_sp_vport, vr->id, l3_dev, rif_index,
+                                false);
+       mlxsw_sp_vr_put(vr);
+}
+
+static int mlxsw_sp_vport_rif_sp_join(struct mlxsw_sp_port *mlxsw_sp_vport,
+                                     struct net_device *l3_dev)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp;
+       struct mlxsw_sp_rif *rif;
+
+       rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
+       if (!rif) {
+               rif = mlxsw_sp_vport_rif_sp_create(mlxsw_sp_vport, l3_dev);
+               if (IS_ERR(rif))
+                       return PTR_ERR(rif);
+       }
+
+       mlxsw_sp_vport_fid_set(mlxsw_sp_vport, rif->f);
+       rif->f->ref_count++;
+
+       netdev_dbg(mlxsw_sp_vport->dev, "Joined FID=%d\n", rif->f->fid);
+
+       return 0;
+}
+
+static void mlxsw_sp_vport_rif_sp_leave(struct mlxsw_sp_port *mlxsw_sp_vport)
+{
+       struct mlxsw_sp_fid *f = mlxsw_sp_vport_fid_get(mlxsw_sp_vport);
+
+       netdev_dbg(mlxsw_sp_vport->dev, "Left FID=%d\n", f->fid);
+
+       mlxsw_sp_vport_fid_set(mlxsw_sp_vport, NULL);
+       if (--f->ref_count == 0)
+               mlxsw_sp_vport_rif_sp_destroy(mlxsw_sp_vport, f->rif);
+}
+
+static int mlxsw_sp_inetaddr_vport_event(struct net_device *l3_dev,
+                                        struct net_device *port_dev,
+                                        unsigned long event, u16 vid)
+{
+       struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(port_dev);
+       struct mlxsw_sp_port *mlxsw_sp_vport;
+
+       mlxsw_sp_vport = mlxsw_sp_port_vport_find(mlxsw_sp_port, vid);
+       if (WARN_ON(!mlxsw_sp_vport))
+               return -EINVAL;
+
+       switch (event) {
+       case NETDEV_UP:
+               return mlxsw_sp_vport_rif_sp_join(mlxsw_sp_vport, l3_dev);
+       case NETDEV_DOWN:
+               mlxsw_sp_vport_rif_sp_leave(mlxsw_sp_vport);
+               break;
+       }
+
+       return 0;
+}
+
+static int mlxsw_sp_inetaddr_port_event(struct net_device *port_dev,
+                                       unsigned long event)
+{
+       if (netif_is_bridge_port(port_dev) ||
+           netif_is_lag_port(port_dev) ||
+           netif_is_ovs_port(port_dev))
+               return 0;
+
+       return mlxsw_sp_inetaddr_vport_event(port_dev, port_dev, event, 1);
+}
+
+static int __mlxsw_sp_inetaddr_lag_event(struct net_device *l3_dev,
+                                        struct net_device *lag_dev,
+                                        unsigned long event, u16 vid)
+{
+       struct net_device *port_dev;
+       struct list_head *iter;
+       int err;
+
+       netdev_for_each_lower_dev(lag_dev, port_dev, iter) {
+               if (mlxsw_sp_port_dev_check(port_dev)) {
+                       err = mlxsw_sp_inetaddr_vport_event(l3_dev, port_dev,
+                                                           event, vid);
+                       if (err)
+                               return err;
+               }
+       }
+
+       return 0;
+}
+
+static int mlxsw_sp_inetaddr_lag_event(struct net_device *lag_dev,
+                                      unsigned long event)
+{
+       if (netif_is_bridge_port(lag_dev))
+               return 0;
+
+       return __mlxsw_sp_inetaddr_lag_event(lag_dev, lag_dev, event, 1);
+}
+
+static struct mlxsw_sp_fid *mlxsw_sp_bridge_fid_get(struct mlxsw_sp *mlxsw_sp,
+                                                   struct net_device *l3_dev)
+{
+       u16 fid;
+
+       if (is_vlan_dev(l3_dev))
+               fid = vlan_dev_vlan_id(l3_dev);
+       else if (mlxsw_sp->master_bridge.dev == l3_dev)
+               fid = 1;
+       else
+               return mlxsw_sp_vfid_find(mlxsw_sp, l3_dev);
+
+       return mlxsw_sp_fid_find(mlxsw_sp, fid);
+}
+
+static u8 mlxsw_sp_router_port(const struct mlxsw_sp *mlxsw_sp)
+{
+       return mlxsw_core_max_ports(mlxsw_sp->core) + 1;
+}
+
+static enum mlxsw_flood_table_type mlxsw_sp_flood_table_type_get(u16 fid)
+{
+       return mlxsw_sp_fid_is_vfid(fid) ? MLXSW_REG_SFGC_TABLE_TYPE_FID :
+              MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST;
+}
+
+static u16 mlxsw_sp_flood_table_index_get(u16 fid)
+{
+       return mlxsw_sp_fid_is_vfid(fid) ? mlxsw_sp_fid_to_vfid(fid) : fid;
+}
+
+static int mlxsw_sp_router_port_flood_set(struct mlxsw_sp *mlxsw_sp, u16 fid,
+                                         bool set)
+{
+       u8 router_port = mlxsw_sp_router_port(mlxsw_sp);
+       enum mlxsw_flood_table_type table_type;
+       char *sftr_pl;
+       u16 index;
+       int err;
+
+       sftr_pl = kmalloc(MLXSW_REG_SFTR_LEN, GFP_KERNEL);
+       if (!sftr_pl)
+               return -ENOMEM;
+
+       table_type = mlxsw_sp_flood_table_type_get(fid);
+       index = mlxsw_sp_flood_table_index_get(fid);
+       mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_BC, index, table_type,
+                           1, router_port, set);
+       err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl);
+
+       kfree(sftr_pl);
+       return err;
+}
+
+static enum mlxsw_reg_ritr_if_type mlxsw_sp_rif_type_get(u16 fid)
+{
+       if (mlxsw_sp_fid_is_vfid(fid))
+               return MLXSW_REG_RITR_FID_IF;
+       else
+               return MLXSW_REG_RITR_VLAN_IF;
+}
+
+static int mlxsw_sp_rif_bridge_op(struct mlxsw_sp *mlxsw_sp, u16 vr_id,
+                                 struct net_device *l3_dev,
+                                 u16 fid, u16 rif,
+                                 bool create)
+{
+       enum mlxsw_reg_ritr_if_type rif_type;
+       char ritr_pl[MLXSW_REG_RITR_LEN];
+
+       rif_type = mlxsw_sp_rif_type_get(fid);
+       mlxsw_reg_ritr_pack(ritr_pl, create, rif_type, rif, vr_id, l3_dev->mtu,
+                           l3_dev->dev_addr);
+       mlxsw_reg_ritr_fid_set(ritr_pl, rif_type, fid);
+
+       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
+}
+
+static int mlxsw_sp_rif_bridge_create(struct mlxsw_sp *mlxsw_sp,
+                                     struct net_device *l3_dev,
+                                     struct mlxsw_sp_fid *f)
+{
+       u32 tb_id = l3mdev_fib_table(l3_dev);
+       struct mlxsw_sp_rif *rif;
+       struct mlxsw_sp_vr *vr;
+       u16 rif_index;
+       int err;
+
+       rif_index = mlxsw_sp_avail_rif_get(mlxsw_sp);
+       if (rif_index == MLXSW_SP_INVALID_INDEX_RIF)
+               return -ERANGE;
+
+       vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id ? : RT_TABLE_MAIN);
+       if (IS_ERR(vr))
+               return PTR_ERR(vr);
+
+       err = mlxsw_sp_router_port_flood_set(mlxsw_sp, f->fid, true);
+       if (err)
+               goto err_port_flood_set;
+
+       err = mlxsw_sp_rif_bridge_op(mlxsw_sp, vr->id, l3_dev, f->fid,
+                                    rif_index, true);
+       if (err)
+               goto err_rif_bridge_op;
+
+       err = mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, f->fid, true);
+       if (err)
+               goto err_rif_fdb_op;
+
+       rif = mlxsw_sp_rif_alloc(rif_index, vr->id, l3_dev, f);
+       if (!rif) {
+               err = -ENOMEM;
+               goto err_rif_alloc;
+       }
+
+       f->rif = rif;
+       mlxsw_sp->rifs[rif_index] = rif;
+       vr->rif_count++;
+
+       netdev_dbg(l3_dev, "RIF=%d created\n", rif_index);
+
+       return 0;
+
+err_rif_alloc:
+       mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, f->fid, false);
+err_rif_fdb_op:
+       mlxsw_sp_rif_bridge_op(mlxsw_sp, vr->id, l3_dev, f->fid, rif_index,
+                              false);
+err_rif_bridge_op:
+       mlxsw_sp_router_port_flood_set(mlxsw_sp, f->fid, false);
+err_port_flood_set:
+       mlxsw_sp_vr_put(vr);
+       return err;
+}
+
+void mlxsw_sp_rif_bridge_destroy(struct mlxsw_sp *mlxsw_sp,
+                                struct mlxsw_sp_rif *rif)
+{
+       struct mlxsw_sp_vr *vr = &mlxsw_sp->router.vrs[rif->vr_id];
+       struct net_device *l3_dev = rif->dev;
+       struct mlxsw_sp_fid *f = rif->f;
+       u16 rif_index = rif->rif_index;
+
+       mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif);
+
+       vr->rif_count--;
+       mlxsw_sp->rifs[rif_index] = NULL;
+       f->rif = NULL;
+
+       kfree(rif);
+
+       mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, f->fid, false);
+
+       mlxsw_sp_rif_bridge_op(mlxsw_sp, vr->id, l3_dev, f->fid, rif_index,
+                              false);
+
+       mlxsw_sp_router_port_flood_set(mlxsw_sp, f->fid, false);
+
+       mlxsw_sp_vr_put(vr);
+
+       netdev_dbg(l3_dev, "RIF=%d destroyed\n", rif_index);
+}
+
+static int mlxsw_sp_inetaddr_bridge_event(struct net_device *l3_dev,
+                                         struct net_device *br_dev,
+                                         unsigned long event)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
+       struct mlxsw_sp_fid *f;
+
+       /* FID can either be an actual FID if the L3 device is the
+        * VLAN-aware bridge or a VLAN device on top. Otherwise, the
+        * L3 device is a VLAN-unaware bridge and we get a vFID.
+        */
+       f = mlxsw_sp_bridge_fid_get(mlxsw_sp, l3_dev);
+       if (WARN_ON(!f))
+               return -EINVAL;
+
+       switch (event) {
+       case NETDEV_UP:
+               return mlxsw_sp_rif_bridge_create(mlxsw_sp, l3_dev, f);
+       case NETDEV_DOWN:
+               mlxsw_sp_rif_bridge_destroy(mlxsw_sp, f->rif);
+               break;
+       }
+
+       return 0;
+}
+
+static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev,
+                                       unsigned long event)
+{
+       struct net_device *real_dev = vlan_dev_real_dev(vlan_dev);
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(vlan_dev);
+       u16 vid = vlan_dev_vlan_id(vlan_dev);
+
+       if (mlxsw_sp_port_dev_check(real_dev))
+               return mlxsw_sp_inetaddr_vport_event(vlan_dev, real_dev, event,
+                                                    vid);
+       else if (netif_is_lag_master(real_dev))
+               return __mlxsw_sp_inetaddr_lag_event(vlan_dev, real_dev, event,
+                                                    vid);
+       else if (netif_is_bridge_master(real_dev) &&
+                mlxsw_sp->master_bridge.dev == real_dev)
+               return mlxsw_sp_inetaddr_bridge_event(vlan_dev, real_dev,
+                                                     event);
+
+       return 0;
+}
+
+static int __mlxsw_sp_inetaddr_event(struct net_device *dev,
+                                    unsigned long event)
+{
+       if (mlxsw_sp_port_dev_check(dev))
+               return mlxsw_sp_inetaddr_port_event(dev, event);
+       else if (netif_is_lag_master(dev))
+               return mlxsw_sp_inetaddr_lag_event(dev, event);
+       else if (netif_is_bridge_master(dev))
+               return mlxsw_sp_inetaddr_bridge_event(dev, dev, event);
+       else if (is_vlan_dev(dev))
+               return mlxsw_sp_inetaddr_vlan_event(dev, event);
+       else
+               return 0;
+}
+
+int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
+                           unsigned long event, void *ptr)
+{
+       struct in_ifaddr *ifa = (struct in_ifaddr *) ptr;
+       struct net_device *dev = ifa->ifa_dev->dev;
+       struct mlxsw_sp *mlxsw_sp;
+       struct mlxsw_sp_rif *rif;
+       int err = 0;
+
+       mlxsw_sp = mlxsw_sp_lower_get(dev);
+       if (!mlxsw_sp)
+               goto out;
+
+       rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
+       if (!mlxsw_sp_rif_should_config(rif, ifa->ifa_dev, event))
+               goto out;
+
+       err = __mlxsw_sp_inetaddr_event(dev, event);
+out:
+       return notifier_from_errno(err);
+}
+
+static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
+                            const char *mac, int mtu)
+{
+       char ritr_pl[MLXSW_REG_RITR_LEN];
+       int err;
+
+       mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
+       err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
+       if (err)
+               return err;
+
+       mlxsw_reg_ritr_mtu_set(ritr_pl, mtu);
+       mlxsw_reg_ritr_if_mac_memcpy_to(ritr_pl, mac);
+       mlxsw_reg_ritr_op_set(ritr_pl, MLXSW_REG_RITR_RIF_CREATE);
+       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
+}
+
+int mlxsw_sp_netdevice_router_port_event(struct net_device *dev)
+{
+       struct mlxsw_sp *mlxsw_sp;
+       struct mlxsw_sp_rif *rif;
+       int err;
+
+       mlxsw_sp = mlxsw_sp_lower_get(dev);
+       if (!mlxsw_sp)
+               return 0;
+
+       rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
+       if (!rif)
+               return 0;
+
+       err = mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, rif->f->fid, false);
+       if (err)
+               return err;
+
+       err = mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, dev->dev_addr,
+                               dev->mtu);
+       if (err)
+               goto err_rif_edit;
+
+       err = mlxsw_sp_rif_fdb_op(mlxsw_sp, dev->dev_addr, rif->f->fid, true);
+       if (err)
+               goto err_rif_fdb_op;
+
+       ether_addr_copy(rif->addr, dev->dev_addr);
+       rif->mtu = dev->mtu;
+
+       netdev_dbg(dev, "Updated RIF=%d\n", rif->rif_index);
+
+       return 0;
+
+err_rif_fdb_op:
+       mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, rif->addr, rif->mtu);
+err_rif_edit:
+       mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, rif->f->fid, true);
+       return err;
+}
+
+static int mlxsw_sp_port_vrf_join(struct mlxsw_sp *mlxsw_sp,
+                                 struct net_device *l3_dev)
+{
+       struct mlxsw_sp_rif *rif;
+
+       /* If netdev is already associated with a RIF, then we need to
+        * destroy it and create a new one with the new virtual router ID.
+        */
+       rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
+       if (rif)
+               __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
+
+       return __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_UP);
+}
+
+static void mlxsw_sp_port_vrf_leave(struct mlxsw_sp *mlxsw_sp,
+                                   struct net_device *l3_dev)
+{
+       struct mlxsw_sp_rif *rif;
+
+       rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev);
+       if (!rif)
+               return;
+       __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN);
+}
+
+int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
+                                struct netdev_notifier_changeupper_info *info)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
+       int err = 0;
+
+       if (!mlxsw_sp)
+               return 0;
+
+       switch (event) {
+       case NETDEV_PRECHANGEUPPER:
+               return 0;
+       case NETDEV_CHANGEUPPER:
+               if (info->linking)
+                       err = mlxsw_sp_port_vrf_join(mlxsw_sp, l3_dev);
+               else
+                       mlxsw_sp_port_vrf_leave(mlxsw_sp, l3_dev);
+               break;
+       }
+
+       return err;
+}
+
 static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb)
 {
        struct mlxsw_sp *mlxsw_sp = container_of(nb, struct mlxsw_sp, fib_nb);
@@ -2606,6 +3504,48 @@ static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb)
        mlxsw_sp_router_fib_flush(mlxsw_sp);
 }
 
+static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
+{
+       char rgcr_pl[MLXSW_REG_RGCR_LEN];
+       u64 max_rifs;
+       int err;
+
+       if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_RIFS))
+               return -EIO;
+
+       max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
+       mlxsw_sp->rifs = kcalloc(max_rifs, 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, max_rifs);
+       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)
+{
+       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);
+
+       for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++)
+               WARN_ON_ONCE(mlxsw_sp->rifs[i]);
+
+       kfree(mlxsw_sp->rifs);
+}
+
 int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
 {
        int err;
@@ -2625,7 +3565,10 @@ int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
        if (err)
                goto err_nexthop_group_ht_init;
 
-       mlxsw_sp_lpm_init(mlxsw_sp);
+       err = mlxsw_sp_lpm_init(mlxsw_sp);
+       if (err)
+               goto err_lpm_init;
+
        err = mlxsw_sp_vrs_init(mlxsw_sp);
        if (err)
                goto err_vrs_init;
@@ -2647,6 +3590,8 @@ err_register_fib_notifier:
 err_neigh_init:
        mlxsw_sp_vrs_fini(mlxsw_sp);
 err_vrs_init:
+       mlxsw_sp_lpm_fini(mlxsw_sp);
+err_lpm_init:
        rhashtable_destroy(&mlxsw_sp->router.nexthop_group_ht);
 err_nexthop_group_ht_init:
        rhashtable_destroy(&mlxsw_sp->router.nexthop_ht);
@@ -2660,6 +3605,7 @@ 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_lpm_fini(mlxsw_sp);
        rhashtable_destroy(&mlxsw_sp->router.nexthop_group_ht);
        rhashtable_destroy(&mlxsw_sp->router.nexthop_ht);
        __mlxsw_sp_router_fini(mlxsw_sp);