mlxsw: spectrum_router: Pass ifindex to mlxsw_sp_ipip_entry_find_by_decap()
[linux-2.6-microblaze.git] / drivers / net / ethernet / mellanox / mlxsw / spectrum_router.c
index 4381f8c..89b44dc 100644 (file)
@@ -352,6 +352,7 @@ enum mlxsw_sp_fib_entry_type {
        MLXSW_SP_FIB_ENTRY_TYPE_NVE_DECAP,
 };
 
+struct mlxsw_sp_nexthop_group_info;
 struct mlxsw_sp_nexthop_group;
 struct mlxsw_sp_fib_entry;
 
@@ -368,18 +369,71 @@ struct mlxsw_sp_fib_entry_decap {
        u32 tunnel_index;
 };
 
+static struct mlxsw_sp_fib_entry_priv *
+mlxsw_sp_fib_entry_priv_create(const struct mlxsw_sp_router_ll_ops *ll_ops)
+{
+       struct mlxsw_sp_fib_entry_priv *priv;
+
+       if (!ll_ops->fib_entry_priv_size)
+               /* No need to have priv */
+               return NULL;
+
+       priv = kzalloc(sizeof(*priv) + ll_ops->fib_entry_priv_size, GFP_KERNEL);
+       if (!priv)
+               return ERR_PTR(-ENOMEM);
+       refcount_set(&priv->refcnt, 1);
+       return priv;
+}
+
+static void
+mlxsw_sp_fib_entry_priv_destroy(struct mlxsw_sp_fib_entry_priv *priv)
+{
+       kfree(priv);
+}
+
+static void mlxsw_sp_fib_entry_priv_hold(struct mlxsw_sp_fib_entry_priv *priv)
+{
+       refcount_inc(&priv->refcnt);
+}
+
+static void mlxsw_sp_fib_entry_priv_put(struct mlxsw_sp_fib_entry_priv *priv)
+{
+       if (!priv || !refcount_dec_and_test(&priv->refcnt))
+               return;
+       mlxsw_sp_fib_entry_priv_destroy(priv);
+}
+
+static void mlxsw_sp_fib_entry_op_ctx_priv_hold(struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
+                                               struct mlxsw_sp_fib_entry_priv *priv)
+{
+       if (!priv)
+               return;
+       mlxsw_sp_fib_entry_priv_hold(priv);
+       list_add(&priv->list, &op_ctx->fib_entry_priv_list);
+}
+
+static void mlxsw_sp_fib_entry_op_ctx_priv_put_all(struct mlxsw_sp_fib_entry_op_ctx *op_ctx)
+{
+       struct mlxsw_sp_fib_entry_priv *priv, *tmp;
+
+       list_for_each_entry_safe(priv, tmp, &op_ctx->fib_entry_priv_list, list)
+               mlxsw_sp_fib_entry_priv_put(priv);
+       INIT_LIST_HEAD(&op_ctx->fib_entry_priv_list);
+}
+
 struct mlxsw_sp_fib_entry {
        struct mlxsw_sp_fib_node *fib_node;
        enum mlxsw_sp_fib_entry_type type;
        struct list_head nexthop_group_node;
        struct mlxsw_sp_nexthop_group *nh_group;
        struct mlxsw_sp_fib_entry_decap decap; /* Valid for decap entries. */
+       struct mlxsw_sp_fib_entry_priv *priv;
 };
 
 struct mlxsw_sp_fib4_entry {
        struct mlxsw_sp_fib_entry common;
+       struct fib_info *fi;
        u32 tb_id;
-       u32 prio;
        u8 tos;
        u8 type;
 };
@@ -409,6 +463,7 @@ struct mlxsw_sp_fib {
        struct mlxsw_sp_vr *vr;
        struct mlxsw_sp_lpm_tree *lpm_tree;
        enum mlxsw_sp_l3proto proto;
+       const struct mlxsw_sp_router_ll_ops *ll_ops;
 };
 
 struct mlxsw_sp_vr {
@@ -422,12 +477,31 @@ struct mlxsw_sp_vr {
        refcount_t ul_rif_refcnt;
 };
 
+static int mlxsw_sp_router_ll_basic_ralta_write(struct mlxsw_sp *mlxsw_sp, char *xralta_pl)
+{
+       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta),
+                              xralta_pl + MLXSW_REG_XRALTA_RALTA_OFFSET);
+}
+
+static int mlxsw_sp_router_ll_basic_ralst_write(struct mlxsw_sp *mlxsw_sp, char *xralst_pl)
+{
+       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst),
+                              xralst_pl + MLXSW_REG_XRALST_RALST_OFFSET);
+}
+
+static int mlxsw_sp_router_ll_basic_raltb_write(struct mlxsw_sp *mlxsw_sp, char *xraltb_pl)
+{
+       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb),
+                              xraltb_pl + MLXSW_REG_XRALTB_RALTB_OFFSET);
+}
+
 static const struct rhashtable_params mlxsw_sp_fib_ht_params;
 
 static struct mlxsw_sp_fib *mlxsw_sp_fib_create(struct mlxsw_sp *mlxsw_sp,
                                                struct mlxsw_sp_vr *vr,
                                                enum mlxsw_sp_l3proto proto)
 {
+       const struct mlxsw_sp_router_ll_ops *ll_ops = mlxsw_sp->router->proto_ll_ops[proto];
        struct mlxsw_sp_lpm_tree *lpm_tree;
        struct mlxsw_sp_fib *fib;
        int err;
@@ -443,6 +517,7 @@ static struct mlxsw_sp_fib *mlxsw_sp_fib_create(struct mlxsw_sp *mlxsw_sp,
        fib->proto = proto;
        fib->vr = vr;
        fib->lpm_tree = lpm_tree;
+       fib->ll_ops = ll_ops;
        mlxsw_sp_lpm_tree_hold(lpm_tree);
        err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, lpm_tree->id);
        if (err)
@@ -481,33 +556,36 @@ mlxsw_sp_lpm_tree_find_unused(struct mlxsw_sp *mlxsw_sp)
 }
 
 static int mlxsw_sp_lpm_tree_alloc(struct mlxsw_sp *mlxsw_sp,
+                                  const struct mlxsw_sp_router_ll_ops *ll_ops,
                                   struct mlxsw_sp_lpm_tree *lpm_tree)
 {
-       char ralta_pl[MLXSW_REG_RALTA_LEN];
+       char xralta_pl[MLXSW_REG_XRALTA_LEN];
 
-       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);
+       mlxsw_reg_xralta_pack(xralta_pl, true,
+                             (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
+                             lpm_tree->id);
+       return ll_ops->ralta_write(mlxsw_sp, xralta_pl);
 }
 
 static void mlxsw_sp_lpm_tree_free(struct mlxsw_sp *mlxsw_sp,
+                                  const struct mlxsw_sp_router_ll_ops *ll_ops,
                                   struct mlxsw_sp_lpm_tree *lpm_tree)
 {
-       char ralta_pl[MLXSW_REG_RALTA_LEN];
+       char xralta_pl[MLXSW_REG_XRALTA_LEN];
 
-       mlxsw_reg_ralta_pack(ralta_pl, false,
-                            (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
-                            lpm_tree->id);
-       mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
+       mlxsw_reg_xralta_pack(xralta_pl, false,
+                             (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto,
+                             lpm_tree->id);
+       ll_ops->ralta_write(mlxsw_sp, xralta_pl);
 }
 
 static int
 mlxsw_sp_lpm_tree_left_struct_set(struct mlxsw_sp *mlxsw_sp,
+                                 const struct mlxsw_sp_router_ll_ops *ll_ops,
                                  struct mlxsw_sp_prefix_usage *prefix_usage,
                                  struct mlxsw_sp_lpm_tree *lpm_tree)
 {
-       char ralst_pl[MLXSW_REG_RALST_LEN];
+       char xralst_pl[MLXSW_REG_XRALST_LEN];
        u8 root_bin = 0;
        u8 prefix;
        u8 last_prefix = MLXSW_REG_RALST_BIN_NO_CHILD;
@@ -515,19 +593,20 @@ mlxsw_sp_lpm_tree_left_struct_set(struct mlxsw_sp *mlxsw_sp,
        mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage)
                root_bin = prefix;
 
-       mlxsw_reg_ralst_pack(ralst_pl, root_bin, lpm_tree->id);
+       mlxsw_reg_xralst_pack(xralst_pl, root_bin, lpm_tree->id);
        mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) {
                if (prefix == 0)
                        continue;
-               mlxsw_reg_ralst_bin_pack(ralst_pl, prefix, last_prefix,
-                                        MLXSW_REG_RALST_BIN_NO_CHILD);
+               mlxsw_reg_xralst_bin_pack(xralst_pl, prefix, last_prefix,
+                                         MLXSW_REG_RALST_BIN_NO_CHILD);
                last_prefix = prefix;
        }
-       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
+       return ll_ops->ralst_write(mlxsw_sp, xralst_pl);
 }
 
 static struct mlxsw_sp_lpm_tree *
 mlxsw_sp_lpm_tree_create(struct mlxsw_sp *mlxsw_sp,
+                        const struct mlxsw_sp_router_ll_ops *ll_ops,
                         struct mlxsw_sp_prefix_usage *prefix_usage,
                         enum mlxsw_sp_l3proto proto)
 {
@@ -538,12 +617,11 @@ mlxsw_sp_lpm_tree_create(struct mlxsw_sp *mlxsw_sp,
        if (!lpm_tree)
                return ERR_PTR(-EBUSY);
        lpm_tree->proto = proto;
-       err = mlxsw_sp_lpm_tree_alloc(mlxsw_sp, lpm_tree);
+       err = mlxsw_sp_lpm_tree_alloc(mlxsw_sp, ll_ops, lpm_tree);
        if (err)
                return ERR_PTR(err);
 
-       err = mlxsw_sp_lpm_tree_left_struct_set(mlxsw_sp, prefix_usage,
-                                               lpm_tree);
+       err = mlxsw_sp_lpm_tree_left_struct_set(mlxsw_sp, ll_ops, prefix_usage, lpm_tree);
        if (err)
                goto err_left_struct_set;
        memcpy(&lpm_tree->prefix_usage, prefix_usage,
@@ -554,14 +632,15 @@ mlxsw_sp_lpm_tree_create(struct mlxsw_sp *mlxsw_sp,
        return lpm_tree;
 
 err_left_struct_set:
-       mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
+       mlxsw_sp_lpm_tree_free(mlxsw_sp, ll_ops, lpm_tree);
        return ERR_PTR(err);
 }
 
 static void mlxsw_sp_lpm_tree_destroy(struct mlxsw_sp *mlxsw_sp,
+                                     const struct mlxsw_sp_router_ll_ops *ll_ops,
                                      struct mlxsw_sp_lpm_tree *lpm_tree)
 {
-       mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
+       mlxsw_sp_lpm_tree_free(mlxsw_sp, ll_ops, lpm_tree);
 }
 
 static struct mlxsw_sp_lpm_tree *
@@ -569,6 +648,7 @@ mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp,
                      struct mlxsw_sp_prefix_usage *prefix_usage,
                      enum mlxsw_sp_l3proto proto)
 {
+       const struct mlxsw_sp_router_ll_ops *ll_ops = mlxsw_sp->router->proto_ll_ops[proto];
        struct mlxsw_sp_lpm_tree *lpm_tree;
        int i;
 
@@ -582,7 +662,7 @@ mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp,
                        return lpm_tree;
                }
        }
-       return mlxsw_sp_lpm_tree_create(mlxsw_sp, prefix_usage, proto);
+       return mlxsw_sp_lpm_tree_create(mlxsw_sp, ll_ops, prefix_usage, proto);
 }
 
 static void mlxsw_sp_lpm_tree_hold(struct mlxsw_sp_lpm_tree *lpm_tree)
@@ -593,8 +673,11 @@ static void mlxsw_sp_lpm_tree_hold(struct mlxsw_sp_lpm_tree *lpm_tree)
 static void mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp,
                                  struct mlxsw_sp_lpm_tree *lpm_tree)
 {
+       const struct mlxsw_sp_router_ll_ops *ll_ops =
+                               mlxsw_sp->router->proto_ll_ops[lpm_tree->proto];
+
        if (--lpm_tree->ref_count == 0)
-               mlxsw_sp_lpm_tree_destroy(mlxsw_sp, lpm_tree);
+               mlxsw_sp_lpm_tree_destroy(mlxsw_sp, ll_ops, lpm_tree);
 }
 
 #define MLXSW_SP_LPM_TREE_MIN 1 /* tree 0 is reserved */
@@ -684,23 +767,23 @@ static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp)
 static int mlxsw_sp_vr_lpm_tree_bind(struct mlxsw_sp *mlxsw_sp,
                                     const struct mlxsw_sp_fib *fib, u8 tree_id)
 {
-       char raltb_pl[MLXSW_REG_RALTB_LEN];
+       char xraltb_pl[MLXSW_REG_XRALTB_LEN];
 
-       mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id,
-                            (enum mlxsw_reg_ralxx_protocol) fib->proto,
-                            tree_id);
-       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
+       mlxsw_reg_xraltb_pack(xraltb_pl, fib->vr->id,
+                             (enum mlxsw_reg_ralxx_protocol) fib->proto,
+                             tree_id);
+       return fib->ll_ops->raltb_write(mlxsw_sp, xraltb_pl);
 }
 
 static int mlxsw_sp_vr_lpm_tree_unbind(struct mlxsw_sp *mlxsw_sp,
                                       const struct mlxsw_sp_fib *fib)
 {
-       char raltb_pl[MLXSW_REG_RALTB_LEN];
+       char xraltb_pl[MLXSW_REG_XRALTB_LEN];
 
        /* Bind to tree 0 which is default */
-       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);
+       mlxsw_reg_xraltb_pack(xraltb_pl, fib->vr->id,
+                             (enum mlxsw_reg_ralxx_protocol) fib->proto, 0);
+       return fib->ll_ops->raltb_write(mlxsw_sp, xraltb_pl);
 }
 
 static u32 mlxsw_sp_fix_tb_id(u32 tb_id)
@@ -1270,21 +1353,33 @@ mlxsw_sp_ipip_entry_matches_decap(struct mlxsw_sp *mlxsw_sp,
 
 /* Given decap parameters, find the corresponding IPIP entry. */
 static struct mlxsw_sp_ipip_entry *
-mlxsw_sp_ipip_entry_find_by_decap(struct mlxsw_sp *mlxsw_sp,
-                                 const struct net_device *ul_dev,
+mlxsw_sp_ipip_entry_find_by_decap(struct mlxsw_sp *mlxsw_sp, int ul_dev_ifindex,
                                  enum mlxsw_sp_l3proto ul_proto,
                                  union mlxsw_sp_l3addr ul_dip)
 {
-       struct mlxsw_sp_ipip_entry *ipip_entry;
+       struct mlxsw_sp_ipip_entry *ipip_entry = NULL;
+       struct net_device *ul_dev;
+
+       rcu_read_lock();
+
+       ul_dev = dev_get_by_index_rcu(mlxsw_sp_net(mlxsw_sp), ul_dev_ifindex);
+       if (!ul_dev)
+               goto out_unlock;
 
        list_for_each_entry(ipip_entry, &mlxsw_sp->router->ipip_list,
                            ipip_list_node)
                if (mlxsw_sp_ipip_entry_matches_decap(mlxsw_sp, ul_dev,
                                                      ul_proto, ul_dip,
                                                      ipip_entry))
-                       return ipip_entry;
+                       goto out_unlock;
+
+       rcu_read_unlock();
 
        return NULL;
+
+out_unlock:
+       rcu_read_unlock();
+       return ipip_entry;
 }
 
 static bool mlxsw_sp_netdev_ipip_type(const struct mlxsw_sp *mlxsw_sp,
@@ -1370,11 +1465,7 @@ static bool mlxsw_sp_netdevice_ipip_can_offload(struct mlxsw_sp *mlxsw_sp,
        const struct mlxsw_sp_ipip_ops *ops
                = mlxsw_sp->router->ipip_ops_arr[ipipt];
 
-       /* For deciding whether decap should be offloaded, we don't care about
-        * overlay protocol, so ask whether either one is supported.
-        */
-       return ops->can_offload(mlxsw_sp, ol_dev, MLXSW_SP_L3_PROTO_IPV4) ||
-              ops->can_offload(mlxsw_sp, ol_dev, MLXSW_SP_L3_PROTO_IPV6);
+       return ops->can_offload(mlxsw_sp, ol_dev);
 }
 
 static int mlxsw_sp_netdevice_ipip_ol_reg_event(struct mlxsw_sp *mlxsw_sp,
@@ -2749,10 +2840,11 @@ struct mlxsw_sp_nexthop {
        struct list_head neigh_list_node; /* member of neigh entry list */
        struct list_head rif_list_node;
        struct list_head router_list_node;
-       struct mlxsw_sp_nexthop_group *nh_grp; /* pointer back to the group
-                                               * this belongs to
-                                               */
+       struct mlxsw_sp_nexthop_group_info *nhgi; /* pointer back to the group
+                                                  * this nexthop belongs to
+                                                  */
        struct rhash_head ht_node;
+       struct neigh_table *neigh_tbl;
        struct mlxsw_sp_nexthop_key key;
        unsigned char gw_addr[sizeof(struct in6_addr)];
        int ifindex;
@@ -2778,21 +2870,35 @@ struct mlxsw_sp_nexthop {
        bool counter_valid;
 };
 
-struct mlxsw_sp_nexthop_group {
-       void *priv;
-       struct rhash_head ht_node;
-       struct list_head fib_list; /* list of fib entries that use this group */
-       struct neigh_table *neigh_tbl;
-       u8 adj_index_valid:1,
-          gateway:1; /* routes using the group use a gateway */
+enum mlxsw_sp_nexthop_group_type {
+       MLXSW_SP_NEXTHOP_GROUP_TYPE_IPV4,
+       MLXSW_SP_NEXTHOP_GROUP_TYPE_IPV6,
+};
+
+struct mlxsw_sp_nexthop_group_info {
+       struct mlxsw_sp_nexthop_group *nh_grp;
        u32 adj_index;
        u16 ecmp_size;
        u16 count;
        int sum_norm_weight;
+       u8 adj_index_valid:1,
+          gateway:1; /* routes using the group use a gateway */
        struct mlxsw_sp_nexthop nexthops[0];
 #define nh_rif nexthops[0].rif
 };
 
+struct mlxsw_sp_nexthop_group {
+       struct rhash_head ht_node;
+       struct list_head fib_list; /* list of fib entries that use this group */
+       union {
+               struct {
+                       struct fib_info *fi;
+               } ipv4;
+       };
+       struct mlxsw_sp_nexthop_group_info *nhgi;
+       enum mlxsw_sp_nexthop_group_type type;
+};
+
 void mlxsw_sp_nexthop_counter_alloc(struct mlxsw_sp *mlxsw_sp,
                                    struct mlxsw_sp_nexthop *nh)
 {
@@ -2858,18 +2964,18 @@ unsigned char *mlxsw_sp_nexthop_ha(struct mlxsw_sp_nexthop *nh)
 int mlxsw_sp_nexthop_indexes(struct mlxsw_sp_nexthop *nh, u32 *p_adj_index,
                             u32 *p_adj_size, u32 *p_adj_hash_index)
 {
-       struct mlxsw_sp_nexthop_group *nh_grp = nh->nh_grp;
+       struct mlxsw_sp_nexthop_group_info *nhgi = nh->nhgi;
        u32 adj_hash_index = 0;
        int i;
 
-       if (!nh->offloaded || !nh_grp->adj_index_valid)
+       if (!nh->offloaded || !nhgi->adj_index_valid)
                return -EINVAL;
 
-       *p_adj_index = nh_grp->adj_index;
-       *p_adj_size = nh_grp->ecmp_size;
+       *p_adj_index = nhgi->adj_index;
+       *p_adj_size = nhgi->ecmp_size;
 
-       for (i = 0; i < nh_grp->count; i++) {
-               struct mlxsw_sp_nexthop *nh_iter = &nh_grp->nexthops[i];
+       for (i = 0; i < nhgi->count; i++) {
+               struct mlxsw_sp_nexthop *nh_iter = &nhgi->nexthops[i];
 
                if (nh_iter == nh)
                        break;
@@ -2888,11 +2994,11 @@ struct mlxsw_sp_rif *mlxsw_sp_nexthop_rif(struct mlxsw_sp_nexthop *nh)
 
 bool mlxsw_sp_nexthop_group_has_ipip(struct mlxsw_sp_nexthop *nh)
 {
-       struct mlxsw_sp_nexthop_group *nh_grp = nh->nh_grp;
+       struct mlxsw_sp_nexthop_group_info *nhgi = nh->nhgi;
        int i;
 
-       for (i = 0; i < nh_grp->count; i++) {
-               struct mlxsw_sp_nexthop *nh_iter = &nh_grp->nexthops[i];
+       for (i = 0; i < nhgi->count; i++) {
+               struct mlxsw_sp_nexthop *nh_iter = &nhgi->nexthops[i];
 
                if (nh_iter->type == MLXSW_SP_NEXTHOP_TYPE_IPIP)
                        return true;
@@ -2900,14 +3006,8 @@ bool mlxsw_sp_nexthop_group_has_ipip(struct mlxsw_sp_nexthop *nh)
        return false;
 }
 
-static struct fib_info *
-mlxsw_sp_nexthop4_group_fi(const struct mlxsw_sp_nexthop_group *nh_grp)
-{
-       return nh_grp->priv;
-}
-
 struct mlxsw_sp_nexthop_group_cmp_arg {
-       enum mlxsw_sp_l3proto proto;
+       enum mlxsw_sp_nexthop_group_type type;
        union {
                struct fib_info *fi;
                struct mlxsw_sp_fib6_entry *fib6_entry;
@@ -2921,10 +3021,10 @@ mlxsw_sp_nexthop6_group_has_nexthop(const struct mlxsw_sp_nexthop_group *nh_grp,
 {
        int i;
 
-       for (i = 0; i < nh_grp->count; i++) {
+       for (i = 0; i < nh_grp->nhgi->count; i++) {
                const struct mlxsw_sp_nexthop *nh;
 
-               nh = &nh_grp->nexthops[i];
+               nh = &nh_grp->nhgi->nexthops[i];
                if (nh->ifindex == ifindex && nh->nh_weight == weight &&
                    ipv6_addr_equal(gw, (struct in6_addr *) nh->gw_addr))
                        return true;
@@ -2939,7 +3039,7 @@ mlxsw_sp_nexthop6_group_cmp(const struct mlxsw_sp_nexthop_group *nh_grp,
 {
        struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
 
-       if (nh_grp->count != fib6_entry->nrt6)
+       if (nh_grp->nhgi->count != fib6_entry->nrt6)
                return false;
 
        list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
@@ -2964,10 +3064,13 @@ mlxsw_sp_nexthop_group_cmp(struct rhashtable_compare_arg *arg, const void *ptr)
        const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = arg->key;
        const struct mlxsw_sp_nexthop_group *nh_grp = ptr;
 
-       switch (cmp_arg->proto) {
-       case MLXSW_SP_L3_PROTO_IPV4:
-               return cmp_arg->fi != mlxsw_sp_nexthop4_group_fi(nh_grp);
-       case MLXSW_SP_L3_PROTO_IPV6:
+       if (nh_grp->type != cmp_arg->type)
+               return 1;
+
+       switch (cmp_arg->type) {
+       case MLXSW_SP_NEXTHOP_GROUP_TYPE_IPV4:
+               return cmp_arg->fi != nh_grp->ipv4.fi;
+       case MLXSW_SP_NEXTHOP_GROUP_TYPE_IPV6:
                return !mlxsw_sp_nexthop6_group_cmp(nh_grp,
                                                    cmp_arg->fib6_entry);
        default:
@@ -2976,12 +3079,6 @@ mlxsw_sp_nexthop_group_cmp(struct rhashtable_compare_arg *arg, const void *ptr)
        }
 }
 
-static int
-mlxsw_sp_nexthop_group_type(const struct mlxsw_sp_nexthop_group *nh_grp)
-{
-       return nh_grp->neigh_tbl->family;
-}
-
 static u32 mlxsw_sp_nexthop_group_hash_obj(const void *data, u32 len, u32 seed)
 {
        const struct mlxsw_sp_nexthop_group *nh_grp = data;
@@ -2990,14 +3087,14 @@ static u32 mlxsw_sp_nexthop_group_hash_obj(const void *data, u32 len, u32 seed)
        unsigned int val;
        int i;
 
-       switch (mlxsw_sp_nexthop_group_type(nh_grp)) {
-       case AF_INET:
-               fi = mlxsw_sp_nexthop4_group_fi(nh_grp);
+       switch (nh_grp->type) {
+       case MLXSW_SP_NEXTHOP_GROUP_TYPE_IPV4:
+               fi = nh_grp->ipv4.fi;
                return jhash(&fi, sizeof(fi), seed);
-       case AF_INET6:
-               val = nh_grp->count;
-               for (i = 0; i < nh_grp->count; i++) {
-                       nh = &nh_grp->nexthops[i];
+       case MLXSW_SP_NEXTHOP_GROUP_TYPE_IPV6:
+               val = nh_grp->nhgi->count;
+               for (i = 0; i < nh_grp->nhgi->count; i++) {
+                       nh = &nh_grp->nhgi->nexthops[i];
                        val ^= jhash(&nh->ifindex, sizeof(nh->ifindex), seed);
                        val ^= jhash(&nh->gw_addr, sizeof(nh->gw_addr), seed);
                }
@@ -3031,10 +3128,10 @@ mlxsw_sp_nexthop_group_hash(const void *data, u32 len, u32 seed)
 {
        const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = data;
 
-       switch (cmp_arg->proto) {
-       case MLXSW_SP_L3_PROTO_IPV4:
+       switch (cmp_arg->type) {
+       case MLXSW_SP_NEXTHOP_GROUP_TYPE_IPV4:
                return jhash(&cmp_arg->fi, sizeof(cmp_arg->fi), seed);
-       case MLXSW_SP_L3_PROTO_IPV6:
+       case MLXSW_SP_NEXTHOP_GROUP_TYPE_IPV6:
                return mlxsw_sp_nexthop6_group_hash(cmp_arg->fib6_entry, seed);
        default:
                WARN_ON(1);
@@ -3052,8 +3149,8 @@ static const struct rhashtable_params mlxsw_sp_nexthop_group_ht_params = {
 static int mlxsw_sp_nexthop_group_insert(struct mlxsw_sp *mlxsw_sp,
                                         struct mlxsw_sp_nexthop_group *nh_grp)
 {
-       if (mlxsw_sp_nexthop_group_type(nh_grp) == AF_INET6 &&
-           !nh_grp->gateway)
+       if (nh_grp->type == MLXSW_SP_NEXTHOP_GROUP_TYPE_IPV6 &&
+           !nh_grp->nhgi->gateway)
                return 0;
 
        return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_group_ht,
@@ -3064,8 +3161,8 @@ static int mlxsw_sp_nexthop_group_insert(struct mlxsw_sp *mlxsw_sp,
 static void mlxsw_sp_nexthop_group_remove(struct mlxsw_sp *mlxsw_sp,
                                          struct mlxsw_sp_nexthop_group *nh_grp)
 {
-       if (mlxsw_sp_nexthop_group_type(nh_grp) == AF_INET6 &&
-           !nh_grp->gateway)
+       if (nh_grp->type == MLXSW_SP_NEXTHOP_GROUP_TYPE_IPV6 &&
+           !nh_grp->nhgi->gateway)
                return;
 
        rhashtable_remove_fast(&mlxsw_sp->router->nexthop_group_ht,
@@ -3079,7 +3176,7 @@ mlxsw_sp_nexthop4_group_lookup(struct mlxsw_sp *mlxsw_sp,
 {
        struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg;
 
-       cmp_arg.proto = MLXSW_SP_L3_PROTO_IPV4;
+       cmp_arg.type = MLXSW_SP_NEXTHOP_GROUP_TYPE_IPV4;
        cmp_arg.fi = fi;
        return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht,
                                      &cmp_arg,
@@ -3092,7 +3189,7 @@ mlxsw_sp_nexthop6_group_lookup(struct mlxsw_sp *mlxsw_sp,
 {
        struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg;
 
-       cmp_arg.proto = MLXSW_SP_L3_PROTO_IPV6;
+       cmp_arg.type = MLXSW_SP_NEXTHOP_GROUP_TYPE_IPV6;
        cmp_arg.fib6_entry = fib6_entry;
        return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht,
                                      &cmp_arg,
@@ -3151,14 +3248,16 @@ static int mlxsw_sp_adj_index_mass_update(struct mlxsw_sp *mlxsw_sp,
        int err;
 
        list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) {
+               struct mlxsw_sp_nexthop_group_info *nhgi = nh_grp->nhgi;
+
                if (fib == fib_entry->fib_node->fib)
                        continue;
                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,
-                                                       nh_grp->ecmp_size);
+                                                       nhgi->adj_index,
+                                                       nhgi->ecmp_size);
                if (err)
                        return err;
        }
@@ -3229,15 +3328,15 @@ static int mlxsw_sp_nexthop_ipip_update(struct mlxsw_sp *mlxsw_sp,
 
 static int
 mlxsw_sp_nexthop_group_update(struct mlxsw_sp *mlxsw_sp,
-                             struct mlxsw_sp_nexthop_group *nh_grp,
+                             struct mlxsw_sp_nexthop_group_info *nhgi,
                              bool reallocate)
 {
-       u32 adj_index = nh_grp->adj_index; /* base */
+       u32 adj_index = nhgi->adj_index; /* base */
        struct mlxsw_sp_nexthop *nh;
        int i;
 
-       for (i = 0; i < nh_grp->count; i++) {
-               nh = &nh_grp->nexthops[i];
+       for (i = 0; i < nhgi->count; i++) {
+               nh = &nhgi->nexthops[i];
 
                if (!nh->should_offload) {
                        nh->offloaded = 0;
@@ -3337,13 +3436,13 @@ static int mlxsw_sp_fix_adj_grp_size(struct mlxsw_sp *mlxsw_sp,
 }
 
 static void
-mlxsw_sp_nexthop_group_normalize(struct mlxsw_sp_nexthop_group *nh_grp)
+mlxsw_sp_nexthop_group_normalize(struct mlxsw_sp_nexthop_group_info *nhgi)
 {
        int i, g = 0, sum_norm_weight = 0;
        struct mlxsw_sp_nexthop *nh;
 
-       for (i = 0; i < nh_grp->count; i++) {
-               nh = &nh_grp->nexthops[i];
+       for (i = 0; i < nhgi->count; i++) {
+               nh = &nhgi->nexthops[i];
 
                if (!nh->should_offload)
                        continue;
@@ -3353,8 +3452,8 @@ mlxsw_sp_nexthop_group_normalize(struct mlxsw_sp_nexthop_group *nh_grp)
                        g = nh->nh_weight;
        }
 
-       for (i = 0; i < nh_grp->count; i++) {
-               nh = &nh_grp->nexthops[i];
+       for (i = 0; i < nhgi->count; i++) {
+               nh = &nhgi->nexthops[i];
 
                if (!nh->should_offload)
                        continue;
@@ -3362,18 +3461,18 @@ mlxsw_sp_nexthop_group_normalize(struct mlxsw_sp_nexthop_group *nh_grp)
                sum_norm_weight += nh->norm_nh_weight;
        }
 
-       nh_grp->sum_norm_weight = sum_norm_weight;
+       nhgi->sum_norm_weight = sum_norm_weight;
 }
 
 static void
-mlxsw_sp_nexthop_group_rebalance(struct mlxsw_sp_nexthop_group *nh_grp)
+mlxsw_sp_nexthop_group_rebalance(struct mlxsw_sp_nexthop_group_info *nhgi)
 {
-       int total = nh_grp->sum_norm_weight;
-       u16 ecmp_size = nh_grp->ecmp_size;
        int i, weight = 0, lower_bound = 0;
+       int total = nhgi->sum_norm_weight;
+       u16 ecmp_size = nhgi->ecmp_size;
 
-       for (i = 0; i < nh_grp->count; i++) {
-               struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
+       for (i = 0; i < nhgi->count; i++) {
+               struct mlxsw_sp_nexthop *nh = &nhgi->nexthops[i];
                int upper_bound;
 
                if (!nh->should_offload)
@@ -3395,8 +3494,8 @@ mlxsw_sp_nexthop4_group_offload_refresh(struct mlxsw_sp *mlxsw_sp,
 {
        int i;
 
-       for (i = 0; i < nh_grp->count; i++) {
-               struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
+       for (i = 0; i < nh_grp->nhgi->count; i++) {
+               struct mlxsw_sp_nexthop *nh = &nh_grp->nhgi->nexthops[i];
 
                if (nh->offloaded)
                        nh->key.fib_nh->fib_nh_flags |= RTNH_F_OFFLOAD;
@@ -3442,11 +3541,11 @@ static void
 mlxsw_sp_nexthop_group_offload_refresh(struct mlxsw_sp *mlxsw_sp,
                                       struct mlxsw_sp_nexthop_group *nh_grp)
 {
-       switch (mlxsw_sp_nexthop_group_type(nh_grp)) {
-       case AF_INET:
+       switch (nh_grp->type) {
+       case MLXSW_SP_NEXTHOP_GROUP_TYPE_IPV4:
                mlxsw_sp_nexthop4_group_offload_refresh(mlxsw_sp, nh_grp);
                break;
-       case AF_INET6:
+       case MLXSW_SP_NEXTHOP_GROUP_TYPE_IPV6:
                mlxsw_sp_nexthop6_group_offload_refresh(mlxsw_sp, nh_grp);
                break;
        }
@@ -3456,6 +3555,7 @@ static void
 mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp,
                               struct mlxsw_sp_nexthop_group *nh_grp)
 {
+       struct mlxsw_sp_nexthop_group_info *nhgi = nh_grp->nhgi;
        u16 ecmp_size, old_ecmp_size;
        struct mlxsw_sp_nexthop *nh;
        bool offload_change = false;
@@ -3465,13 +3565,13 @@ mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp,
        int i;
        int err;
 
-       if (!nh_grp->gateway) {
+       if (!nhgi->gateway) {
                mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
                return;
        }
 
-       for (i = 0; i < nh_grp->count; i++) {
-               nh = &nh_grp->nexthops[i];
+       for (i = 0; i < nhgi->count; i++) {
+               nh = &nhgi->nexthops[i];
 
                if (nh->should_offload != nh->offloaded) {
                        offload_change = true;
@@ -3483,21 +3583,21 @@ mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp,
                /* Nothing was added or removed, so no need to reallocate. Just
                 * update MAC on existing adjacency indexes.
                 */
-               err = mlxsw_sp_nexthop_group_update(mlxsw_sp, nh_grp, false);
+               err = mlxsw_sp_nexthop_group_update(mlxsw_sp, nhgi, false);
                if (err) {
                        dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
                        goto set_trap;
                }
                return;
        }
-       mlxsw_sp_nexthop_group_normalize(nh_grp);
-       if (!nh_grp->sum_norm_weight)
+       mlxsw_sp_nexthop_group_normalize(nhgi);
+       if (!nhgi->sum_norm_weight)
                /* No neigh of this group is connected so we just set
                 * the trap and let everthing flow through kernel.
                 */
                goto set_trap;
 
-       ecmp_size = nh_grp->sum_norm_weight;
+       ecmp_size = nhgi->sum_norm_weight;
        err = mlxsw_sp_fix_adj_grp_size(mlxsw_sp, &ecmp_size);
        if (err)
                /* No valid allocation size available. */
@@ -3512,14 +3612,14 @@ mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp,
                dev_warn(mlxsw_sp->bus_info->dev, "Failed to allocate KVD linear area for nexthop group.\n");
                goto set_trap;
        }
-       old_adj_index_valid = nh_grp->adj_index_valid;
-       old_adj_index = nh_grp->adj_index;
-       old_ecmp_size = nh_grp->ecmp_size;
-       nh_grp->adj_index_valid = 1;
-       nh_grp->adj_index = adj_index;
-       nh_grp->ecmp_size = ecmp_size;
-       mlxsw_sp_nexthop_group_rebalance(nh_grp);
-       err = mlxsw_sp_nexthop_group_update(mlxsw_sp, nh_grp, true);
+       old_adj_index_valid = nhgi->adj_index_valid;
+       old_adj_index = nhgi->adj_index;
+       old_ecmp_size = nhgi->ecmp_size;
+       nhgi->adj_index_valid = 1;
+       nhgi->adj_index = adj_index;
+       nhgi->ecmp_size = ecmp_size;
+       mlxsw_sp_nexthop_group_rebalance(nhgi);
+       err = mlxsw_sp_nexthop_group_update(mlxsw_sp, nhgi, true);
        if (err) {
                dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n");
                goto set_trap;
@@ -3551,10 +3651,10 @@ mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp,
        return;
 
 set_trap:
-       old_adj_index_valid = nh_grp->adj_index_valid;
-       nh_grp->adj_index_valid = 0;
-       for (i = 0; i < nh_grp->count; i++) {
-               nh = &nh_grp->nexthops[i];
+       old_adj_index_valid = nhgi->adj_index_valid;
+       nhgi->adj_index_valid = 0;
+       for (i = 0; i < nhgi->count; i++) {
+               nh = &nhgi->nexthops[i];
                nh->offloaded = 0;
        }
        err = mlxsw_sp_nexthop_fib_entries_update(mlxsw_sp, nh_grp);
@@ -3563,7 +3663,7 @@ set_trap:
        mlxsw_sp_nexthop_group_offload_refresh(mlxsw_sp, nh_grp);
        if (old_adj_index_valid)
                mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ,
-                                  nh_grp->ecmp_size, nh_grp->adj_index);
+                                  nhgi->ecmp_size, nhgi->adj_index);
 }
 
 static void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh,
@@ -3589,10 +3689,9 @@ mlxsw_sp_nexthop_dead_neigh_replace(struct mlxsw_sp *mlxsw_sp,
        nh = list_first_entry(&neigh_entry->nexthop_list,
                              struct mlxsw_sp_nexthop, neigh_list_node);
 
-       n = neigh_lookup(nh->nh_grp->neigh_tbl, &nh->gw_addr, nh->rif->dev);
+       n = neigh_lookup(nh->neigh_tbl, &nh->gw_addr, nh->rif->dev);
        if (!n) {
-               n = neigh_create(nh->nh_grp->neigh_tbl, &nh->gw_addr,
-                                nh->rif->dev);
+               n = neigh_create(nh->neigh_tbl, &nh->gw_addr, nh->rif->dev);
                if (IS_ERR(n))
                        return PTR_ERR(n);
                neigh_event_send(n, NULL);
@@ -3615,7 +3714,7 @@ mlxsw_sp_nexthop_dead_neigh_replace(struct mlxsw_sp *mlxsw_sp,
                neigh_release(old_n);
                neigh_clone(n);
                __mlxsw_sp_nexthop_neigh_update(nh, !entry_connected);
-               mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
+               mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nhgi->nh_grp);
        }
 
        neigh_release(n);
@@ -3652,7 +3751,7 @@ mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
        list_for_each_entry(nh, &neigh_entry->nexthop_list,
                            neigh_list_node) {
                __mlxsw_sp_nexthop_neigh_update(nh, removing);
-               mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
+               mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nhgi->nh_grp);
        }
 }
 
@@ -3683,7 +3782,7 @@ static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp,
        u8 nud_state, dead;
        int err;
 
-       if (!nh->nh_grp->gateway || nh->neigh_entry)
+       if (!nh->nhgi->gateway || nh->neigh_entry)
                return 0;
 
        /* Take a reference of neigh here ensuring that neigh would
@@ -3691,10 +3790,9 @@ static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp,
         * The reference is taken either in neigh_lookup() or
         * in neigh_create() in case n is not found.
         */
-       n = neigh_lookup(nh->nh_grp->neigh_tbl, &nh->gw_addr, nh->rif->dev);
+       n = neigh_lookup(nh->neigh_tbl, &nh->gw_addr, nh->rif->dev);
        if (!n) {
-               n = neigh_create(nh->nh_grp->neigh_tbl, &nh->gw_addr,
-                                nh->rif->dev);
+               n = neigh_create(nh->neigh_tbl, &nh->gw_addr, nh->rif->dev);
                if (IS_ERR(n))
                        return PTR_ERR(n);
                neigh_event_send(n, NULL);
@@ -3775,7 +3873,7 @@ static void mlxsw_sp_nexthop_ipip_init(struct mlxsw_sp *mlxsw_sp,
 {
        bool removing;
 
-       if (!nh->nh_grp->gateway || nh->ipip_entry)
+       if (!nh->nhgi->gateway || nh->ipip_entry)
                return;
 
        nh->ipip_entry = ipip_entry;
@@ -3807,27 +3905,11 @@ static bool mlxsw_sp_nexthop4_ipip_type(const struct mlxsw_sp *mlxsw_sp,
               mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, p_ipipt);
 }
 
-static void mlxsw_sp_nexthop_type_fini(struct mlxsw_sp *mlxsw_sp,
-                                      struct mlxsw_sp_nexthop *nh)
-{
-       switch (nh->type) {
-       case MLXSW_SP_NEXTHOP_TYPE_ETH:
-               mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
-               mlxsw_sp_nexthop_rif_fini(nh);
-               break;
-       case MLXSW_SP_NEXTHOP_TYPE_IPIP:
-               mlxsw_sp_nexthop_rif_fini(nh);
-               mlxsw_sp_nexthop_ipip_fini(mlxsw_sp, nh);
-               break;
-       }
-}
-
-static int mlxsw_sp_nexthop4_type_init(struct mlxsw_sp *mlxsw_sp,
-                                      struct mlxsw_sp_nexthop *nh,
-                                      struct fib_nh *fib_nh)
+static int mlxsw_sp_nexthop_type_init(struct mlxsw_sp *mlxsw_sp,
+                                     struct mlxsw_sp_nexthop *nh,
+                                     const struct net_device *dev)
 {
        const struct mlxsw_sp_ipip_ops *ipip_ops;
-       struct net_device *dev = fib_nh->fib_nh_dev;
        struct mlxsw_sp_ipip_entry *ipip_entry;
        struct mlxsw_sp_rif *rif;
        int err;
@@ -3835,8 +3917,7 @@ static int mlxsw_sp_nexthop4_type_init(struct mlxsw_sp *mlxsw_sp,
        ipip_entry = mlxsw_sp_ipip_entry_find_by_ol_dev(mlxsw_sp, dev);
        if (ipip_entry) {
                ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt];
-               if (ipip_ops->can_offload(mlxsw_sp, dev,
-                                         MLXSW_SP_L3_PROTO_IPV4)) {
+               if (ipip_ops->can_offload(mlxsw_sp, dev)) {
                        nh->type = MLXSW_SP_NEXTHOP_TYPE_IPIP;
                        mlxsw_sp_nexthop_ipip_init(mlxsw_sp, nh, ipip_entry);
                        return 0;
@@ -3860,10 +3941,19 @@ err_neigh_init:
        return err;
 }
 
-static void mlxsw_sp_nexthop4_type_fini(struct mlxsw_sp *mlxsw_sp,
-                                       struct mlxsw_sp_nexthop *nh)
+static void mlxsw_sp_nexthop_type_fini(struct mlxsw_sp *mlxsw_sp,
+                                      struct mlxsw_sp_nexthop *nh)
 {
-       mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);
+       switch (nh->type) {
+       case MLXSW_SP_NEXTHOP_TYPE_ETH:
+               mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
+               mlxsw_sp_nexthop_rif_fini(nh);
+               break;
+       case MLXSW_SP_NEXTHOP_TYPE_IPIP:
+               mlxsw_sp_nexthop_rif_fini(nh);
+               mlxsw_sp_nexthop_ipip_fini(mlxsw_sp, nh);
+               break;
+       }
 }
 
 static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp,
@@ -3875,7 +3965,7 @@ static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp,
        struct in_device *in_dev;
        int err;
 
-       nh->nh_grp = nh_grp;
+       nh->nhgi = nh_grp->nhgi;
        nh->key.fib_nh = fib_nh;
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
        nh->nh_weight = fib_nh->fib_nh_weight;
@@ -3883,6 +3973,7 @@ static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp,
        nh->nh_weight = 1;
 #endif
        memcpy(&nh->gw_addr, &fib_nh->fib_nh_gw4, sizeof(fib_nh->fib_nh_gw4));
+       nh->neigh_tbl = &arp_tbl;
        err = mlxsw_sp_nexthop_insert(mlxsw_sp, nh);
        if (err)
                return err;
@@ -3892,6 +3983,7 @@ static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp,
 
        if (!dev)
                return 0;
+       nh->ifindex = dev->ifindex;
 
        rcu_read_lock();
        in_dev = __in_dev_get_rcu(dev);
@@ -3902,7 +3994,7 @@ static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp,
        }
        rcu_read_unlock();
 
-       err = mlxsw_sp_nexthop4_type_init(mlxsw_sp, nh, fib_nh);
+       err = mlxsw_sp_nexthop_type_init(mlxsw_sp, nh, dev);
        if (err)
                goto err_nexthop_neigh_init;
 
@@ -3916,7 +4008,7 @@ err_nexthop_neigh_init:
 static void mlxsw_sp_nexthop4_fini(struct mlxsw_sp *mlxsw_sp,
                                   struct mlxsw_sp_nexthop *nh)
 {
-       mlxsw_sp_nexthop4_type_fini(mlxsw_sp, nh);
+       mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);
        list_del(&nh->router_list_node);
        mlxsw_sp_nexthop_counter_free(mlxsw_sp, nh);
        mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
@@ -3938,14 +4030,14 @@ static void mlxsw_sp_nexthop4_event(struct mlxsw_sp *mlxsw_sp,
 
        switch (event) {
        case FIB_EVENT_NH_ADD:
-               mlxsw_sp_nexthop4_type_init(mlxsw_sp, nh, fib_nh);
+               mlxsw_sp_nexthop_type_init(mlxsw_sp, nh, fib_nh->fib_nh_dev);
                break;
        case FIB_EVENT_NH_DEL:
-               mlxsw_sp_nexthop4_type_fini(mlxsw_sp, nh);
+               mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);
                break;
        }
 
-       mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
+       mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nhgi->nh_grp);
 }
 
 static void mlxsw_sp_nexthop_rif_update(struct mlxsw_sp *mlxsw_sp,
@@ -3968,7 +4060,7 @@ static void mlxsw_sp_nexthop_rif_update(struct mlxsw_sp *mlxsw_sp,
                }
 
                __mlxsw_sp_nexthop_neigh_update(nh, removing);
-               mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
+               mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nhgi->nh_grp);
        }
 }
 
@@ -3991,7 +4083,7 @@ static void mlxsw_sp_nexthop_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
 
        list_for_each_entry_safe(nh, tmp, &rif->nexthop_list, rif_list_node) {
                mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);
-               mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
+               mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nhgi->nh_grp);
        }
 }
 
@@ -4004,45 +4096,88 @@ static bool mlxsw_sp_fi_is_gateway(const struct mlxsw_sp *mlxsw_sp,
               mlxsw_sp_nexthop4_ipip_type(mlxsw_sp, nh, NULL);
 }
 
+static int
+mlxsw_sp_nexthop4_group_info_init(struct mlxsw_sp *mlxsw_sp,
+                                 struct mlxsw_sp_nexthop_group *nh_grp)
+{
+       unsigned int nhs = fib_info_num_path(nh_grp->ipv4.fi);
+       struct mlxsw_sp_nexthop_group_info *nhgi;
+       struct mlxsw_sp_nexthop *nh;
+       int err, i;
+
+       nhgi = kzalloc(struct_size(nhgi, nexthops, nhs), GFP_KERNEL);
+       if (!nhgi)
+               return -ENOMEM;
+       nh_grp->nhgi = nhgi;
+       nhgi->nh_grp = nh_grp;
+       nhgi->gateway = mlxsw_sp_fi_is_gateway(mlxsw_sp, nh_grp->ipv4.fi);
+       nhgi->count = nhs;
+       for (i = 0; i < nhgi->count; i++) {
+               struct fib_nh *fib_nh;
+
+               nh = &nhgi->nexthops[i];
+               fib_nh = fib_info_nh(nh_grp->ipv4.fi, i);
+               err = mlxsw_sp_nexthop4_init(mlxsw_sp, nh_grp, nh, fib_nh);
+               if (err)
+                       goto err_nexthop4_init;
+       }
+       mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
+
+       return 0;
+
+err_nexthop4_init:
+       for (i--; i >= 0; i--) {
+               nh = &nhgi->nexthops[i];
+               mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
+       }
+       kfree(nhgi);
+       return err;
+}
+
+static void
+mlxsw_sp_nexthop4_group_info_fini(struct mlxsw_sp *mlxsw_sp,
+                                 struct mlxsw_sp_nexthop_group *nh_grp)
+{
+       struct mlxsw_sp_nexthop_group_info *nhgi = nh_grp->nhgi;
+       int i;
+
+       for (i = nhgi->count - 1; i >= 0; i--) {
+               struct mlxsw_sp_nexthop *nh = &nhgi->nexthops[i];
+
+               mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
+       }
+       mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
+       WARN_ON_ONCE(nhgi->adj_index_valid);
+       kfree(nhgi);
+}
+
 static struct mlxsw_sp_nexthop_group *
 mlxsw_sp_nexthop4_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi)
 {
-       unsigned int nhs = fib_info_num_path(fi);
        struct mlxsw_sp_nexthop_group *nh_grp;
-       struct mlxsw_sp_nexthop *nh;
-       struct fib_nh *fib_nh;
-       int i;
        int err;
 
-       nh_grp = kzalloc(struct_size(nh_grp, nexthops, nhs), GFP_KERNEL);
+       nh_grp = kzalloc(sizeof(*nh_grp), GFP_KERNEL);
        if (!nh_grp)
                return ERR_PTR(-ENOMEM);
-       nh_grp->priv = fi;
        INIT_LIST_HEAD(&nh_grp->fib_list);
-       nh_grp->neigh_tbl = &arp_tbl;
-
-       nh_grp->gateway = mlxsw_sp_fi_is_gateway(mlxsw_sp, fi);
-       nh_grp->count = nhs;
+       nh_grp->type = MLXSW_SP_NEXTHOP_GROUP_TYPE_IPV4;
+       nh_grp->ipv4.fi = fi;
        fib_info_hold(fi);
-       for (i = 0; i < nh_grp->count; i++) {
-               nh = &nh_grp->nexthops[i];
-               fib_nh = fib_info_nh(fi, i);
-               err = mlxsw_sp_nexthop4_init(mlxsw_sp, nh_grp, nh, fib_nh);
-               if (err)
-                       goto err_nexthop4_init;
-       }
+
+       err = mlxsw_sp_nexthop4_group_info_init(mlxsw_sp, nh_grp);
+       if (err)
+               goto err_nexthop_group_info_init;
+
        err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
        if (err)
                goto err_nexthop_group_insert;
-       mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
+
        return nh_grp;
 
 err_nexthop_group_insert:
-err_nexthop4_init:
-       for (i--; i >= 0; i--) {
-               nh = &nh_grp->nexthops[i];
-               mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
-       }
+       mlxsw_sp_nexthop4_group_info_fini(mlxsw_sp, nh_grp);
+err_nexthop_group_info_init:
        fib_info_put(fi);
        kfree(nh_grp);
        return ERR_PTR(err);
@@ -4052,17 +4187,9 @@ static void
 mlxsw_sp_nexthop4_group_destroy(struct mlxsw_sp *mlxsw_sp,
                                struct mlxsw_sp_nexthop_group *nh_grp)
 {
-       struct mlxsw_sp_nexthop *nh;
-       int i;
-
        mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
-       for (i = 0; i < nh_grp->count; i++) {
-               nh = &nh_grp->nexthops[i];
-               mlxsw_sp_nexthop4_fini(mlxsw_sp, nh);
-       }
-       mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
-       WARN_ON_ONCE(nh_grp->adj_index_valid);
-       fib_info_put(mlxsw_sp_nexthop4_group_fi(nh_grp));
+       mlxsw_sp_nexthop4_group_info_fini(mlxsw_sp, nh_grp);
+       fib_info_put(nh_grp->ipv4.fi);
        kfree(nh_grp);
 }
 
@@ -4120,9 +4247,9 @@ mlxsw_sp_fib_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
 
        switch (fib_entry->type) {
        case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
-               return !!nh_group->adj_index_valid;
+               return !!nh_group->nhgi->adj_index_valid;
        case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
-               return !!nh_group->nh_rif;
+               return !!nh_group->nhgi->nh_rif;
        case MLXSW_SP_FIB_ENTRY_TYPE_BLACKHOLE:
        case MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP:
        case MLXSW_SP_FIB_ENTRY_TYPE_NVE_DECAP:
@@ -4138,8 +4265,8 @@ mlxsw_sp_rt6_nexthop(struct mlxsw_sp_nexthop_group *nh_grp,
 {
        int i;
 
-       for (i = 0; i < nh_grp->count; i++) {
-               struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
+       for (i = 0; i < nh_grp->nhgi->count; i++) {
+               struct mlxsw_sp_nexthop *nh = &nh_grp->nhgi->nexthops[i];
                struct fib6_info *rt = mlxsw_sp_rt6->rt;
 
                if (nh->rif && nh->rif->dev == rt->fib6_nh->fib_nh_dev &&
@@ -4156,7 +4283,6 @@ static void
 mlxsw_sp_fib4_entry_hw_flags_set(struct mlxsw_sp *mlxsw_sp,
                                 struct mlxsw_sp_fib_entry *fib_entry)
 {
-       struct fib_info *fi = mlxsw_sp_nexthop4_group_fi(fib_entry->nh_group);
        u32 *p_dst = (u32 *) fib_entry->fib_node->key.addr;
        int dst_len = fib_entry->fib_node->key.prefix_len;
        struct mlxsw_sp_fib4_entry *fib4_entry;
@@ -4166,7 +4292,7 @@ mlxsw_sp_fib4_entry_hw_flags_set(struct mlxsw_sp *mlxsw_sp,
        should_offload = mlxsw_sp_fib_entry_should_offload(fib_entry);
        fib4_entry = container_of(fib_entry, struct mlxsw_sp_fib4_entry,
                                  common);
-       fri.fi = fi;
+       fri.fi = fib4_entry->fi;
        fri.tb_id = fib4_entry->tb_id;
        fri.dst = cpu_to_be32(*p_dst);
        fri.dst_len = dst_len;
@@ -4181,7 +4307,6 @@ static void
 mlxsw_sp_fib4_entry_hw_flags_clear(struct mlxsw_sp *mlxsw_sp,
                                   struct mlxsw_sp_fib_entry *fib_entry)
 {
-       struct fib_info *fi = mlxsw_sp_nexthop4_group_fi(fib_entry->nh_group);
        u32 *p_dst = (u32 *) fib_entry->fib_node->key.addr;
        int dst_len = fib_entry->fib_node->key.prefix_len;
        struct mlxsw_sp_fib4_entry *fib4_entry;
@@ -4189,7 +4314,7 @@ mlxsw_sp_fib4_entry_hw_flags_clear(struct mlxsw_sp *mlxsw_sp,
 
        fib4_entry = container_of(fib_entry, struct mlxsw_sp_fib4_entry,
                                  common);
-       fri.fi = fi;
+       fri.fi = fib4_entry->fi;
        fri.tb_id = fib4_entry->tb_id;
        fri.dst = cpu_to_be32(*p_dst);
        fri.dst_len = dst_len;
@@ -4264,13 +4389,14 @@ mlxsw_sp_fib_entry_hw_flags_clear(struct mlxsw_sp *mlxsw_sp,
 static void
 mlxsw_sp_fib_entry_hw_flags_refresh(struct mlxsw_sp *mlxsw_sp,
                                    struct mlxsw_sp_fib_entry *fib_entry,
-                                   enum mlxsw_reg_ralue_op op)
+                                   enum mlxsw_sp_fib_entry_op op)
 {
        switch (op) {
-       case MLXSW_REG_RALUE_OP_WRITE_WRITE:
+       case MLXSW_SP_FIB_ENTRY_OP_WRITE:
+       case MLXSW_SP_FIB_ENTRY_OP_UPDATE:
                mlxsw_sp_fib_entry_hw_flags_set(mlxsw_sp, fib_entry);
                break;
-       case MLXSW_REG_RALUE_OP_WRITE_DELETE:
+       case MLXSW_SP_FIB_ENTRY_OP_DELETE:
                mlxsw_sp_fib_entry_hw_flags_clear(mlxsw_sp, fib_entry);
                break;
        default:
@@ -4278,32 +4404,132 @@ mlxsw_sp_fib_entry_hw_flags_refresh(struct mlxsw_sp *mlxsw_sp,
        }
 }
 
+struct mlxsw_sp_fib_entry_op_ctx_basic {
+       char ralue_pl[MLXSW_REG_RALUE_LEN];
+};
+
 static void
-mlxsw_sp_fib_entry_ralue_pack(char *ralue_pl,
-                             const struct mlxsw_sp_fib_entry *fib_entry,
-                             enum mlxsw_reg_ralue_op op)
+mlxsw_sp_router_ll_basic_fib_entry_pack(struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
+                                       enum mlxsw_sp_l3proto proto,
+                                       enum mlxsw_sp_fib_entry_op op,
+                                       u16 virtual_router, u8 prefix_len,
+                                       unsigned char *addr,
+                                       struct mlxsw_sp_fib_entry_priv *priv)
 {
-       struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib;
-       enum mlxsw_reg_ralxx_protocol proto;
-       u32 *p_dip;
+       struct mlxsw_sp_fib_entry_op_ctx_basic *op_ctx_basic = (void *) op_ctx->ll_priv;
+       enum mlxsw_reg_ralxx_protocol ralxx_proto;
+       char *ralue_pl = op_ctx_basic->ralue_pl;
+       enum mlxsw_reg_ralue_op ralue_op;
 
-       proto = (enum mlxsw_reg_ralxx_protocol) fib->proto;
+       ralxx_proto = (enum mlxsw_reg_ralxx_protocol) proto;
 
-       switch (fib->proto) {
+       switch (op) {
+       case MLXSW_SP_FIB_ENTRY_OP_WRITE:
+       case MLXSW_SP_FIB_ENTRY_OP_UPDATE:
+               ralue_op = MLXSW_REG_RALUE_OP_WRITE_WRITE;
+               break;
+       case MLXSW_SP_FIB_ENTRY_OP_DELETE:
+               ralue_op = MLXSW_REG_RALUE_OP_WRITE_DELETE;
+               break;
+       default:
+               WARN_ON_ONCE(1);
+               return;
+       }
+
+       switch (proto) {
        case MLXSW_SP_L3_PROTO_IPV4:
-               p_dip = (u32 *) fib_entry->fib_node->key.addr;
-               mlxsw_reg_ralue_pack4(ralue_pl, proto, op, fib->vr->id,
-                                     fib_entry->fib_node->key.prefix_len,
-                                     *p_dip);
+               mlxsw_reg_ralue_pack4(ralue_pl, ralxx_proto, ralue_op,
+                                     virtual_router, prefix_len, (u32 *) addr);
                break;
        case MLXSW_SP_L3_PROTO_IPV6:
-               mlxsw_reg_ralue_pack6(ralue_pl, proto, op, fib->vr->id,
-                                     fib_entry->fib_node->key.prefix_len,
-                                     fib_entry->fib_node->key.addr);
+               mlxsw_reg_ralue_pack6(ralue_pl, ralxx_proto, ralue_op,
+                                     virtual_router, prefix_len, addr);
                break;
        }
 }
 
+static void
+mlxsw_sp_router_ll_basic_fib_entry_act_remote_pack(struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
+                                                  enum mlxsw_reg_ralue_trap_action trap_action,
+                                                  u16 trap_id, u32 adjacency_index, u16 ecmp_size)
+{
+       struct mlxsw_sp_fib_entry_op_ctx_basic *op_ctx_basic = (void *) op_ctx->ll_priv;
+
+       mlxsw_reg_ralue_act_remote_pack(op_ctx_basic->ralue_pl, trap_action,
+                                       trap_id, adjacency_index, ecmp_size);
+}
+
+static void
+mlxsw_sp_router_ll_basic_fib_entry_act_local_pack(struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
+                                                 enum mlxsw_reg_ralue_trap_action trap_action,
+                                                 u16 trap_id, u16 local_erif)
+{
+       struct mlxsw_sp_fib_entry_op_ctx_basic *op_ctx_basic = (void *) op_ctx->ll_priv;
+
+       mlxsw_reg_ralue_act_local_pack(op_ctx_basic->ralue_pl, trap_action,
+                                      trap_id, local_erif);
+}
+
+static void
+mlxsw_sp_router_ll_basic_fib_entry_act_ip2me_pack(struct mlxsw_sp_fib_entry_op_ctx *op_ctx)
+{
+       struct mlxsw_sp_fib_entry_op_ctx_basic *op_ctx_basic = (void *) op_ctx->ll_priv;
+
+       mlxsw_reg_ralue_act_ip2me_pack(op_ctx_basic->ralue_pl);
+}
+
+static void
+mlxsw_sp_router_ll_basic_fib_entry_act_ip2me_tun_pack(struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
+                                                     u32 tunnel_ptr)
+{
+       struct mlxsw_sp_fib_entry_op_ctx_basic *op_ctx_basic = (void *) op_ctx->ll_priv;
+
+       mlxsw_reg_ralue_act_ip2me_tun_pack(op_ctx_basic->ralue_pl, tunnel_ptr);
+}
+
+static int
+mlxsw_sp_router_ll_basic_fib_entry_commit(struct mlxsw_sp *mlxsw_sp,
+                                         struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
+                                         bool *postponed_for_bulk)
+{
+       struct mlxsw_sp_fib_entry_op_ctx_basic *op_ctx_basic = (void *) op_ctx->ll_priv;
+
+       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue),
+                              op_ctx_basic->ralue_pl);
+}
+
+static bool
+mlxsw_sp_router_ll_basic_fib_entry_is_committed(struct mlxsw_sp_fib_entry_priv *priv)
+{
+       return true;
+}
+
+static void mlxsw_sp_fib_entry_pack(struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
+                                   struct mlxsw_sp_fib_entry *fib_entry,
+                                   enum mlxsw_sp_fib_entry_op op)
+{
+       struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib;
+
+       mlxsw_sp_fib_entry_op_ctx_priv_hold(op_ctx, fib_entry->priv);
+       fib->ll_ops->fib_entry_pack(op_ctx, fib->proto, op, fib->vr->id,
+                                   fib_entry->fib_node->key.prefix_len,
+                                   fib_entry->fib_node->key.addr,
+                                   fib_entry->priv);
+}
+
+int mlxsw_sp_fib_entry_commit(struct mlxsw_sp *mlxsw_sp,
+                             struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
+                             const struct mlxsw_sp_router_ll_ops *ll_ops)
+{
+       bool postponed_for_bulk = false;
+       int err;
+
+       err = ll_ops->fib_entry_commit(mlxsw_sp, op_ctx, &postponed_for_bulk);
+       if (!postponed_for_bulk)
+               mlxsw_sp_fib_entry_op_ctx_priv_put_all(op_ctx);
+       return err;
+}
+
 static int mlxsw_sp_adj_discard_write(struct mlxsw_sp *mlxsw_sp, u16 rif_index)
 {
        enum mlxsw_reg_ratr_trap_action trap_action;
@@ -4338,11 +4564,13 @@ err_ratr_write:
 }
 
 static int mlxsw_sp_fib_entry_op_remote(struct mlxsw_sp *mlxsw_sp,
+                                       struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
                                        struct mlxsw_sp_fib_entry *fib_entry,
-                                       enum mlxsw_reg_ralue_op op)
+                                       enum mlxsw_sp_fib_entry_op op)
 {
+       const struct mlxsw_sp_router_ll_ops *ll_ops = fib_entry->fib_node->fib->ll_ops;
        struct mlxsw_sp_nexthop_group *nh_group = fib_entry->nh_group;
-       char ralue_pl[MLXSW_REG_RALUE_LEN];
+       struct mlxsw_sp_nexthop_group_info *nhgi = nh_group->nhgi;
        enum mlxsw_reg_ralue_trap_action trap_action;
        u16 trap_id = 0;
        u32 adjacency_index = 0;
@@ -4355,12 +4583,11 @@ static int mlxsw_sp_fib_entry_op_remote(struct mlxsw_sp *mlxsw_sp,
         */
        if (mlxsw_sp_fib_entry_should_offload(fib_entry)) {
                trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
-               adjacency_index = fib_entry->nh_group->adj_index;
-               ecmp_size = fib_entry->nh_group->ecmp_size;
-       } else if (!nh_group->adj_index_valid && nh_group->count &&
-                  nh_group->nh_rif) {
+               adjacency_index = nhgi->adj_index;
+               ecmp_size = nhgi->ecmp_size;
+       } else if (!nhgi->adj_index_valid && nhgi->count && nhgi->nh_rif) {
                err = mlxsw_sp_adj_discard_write(mlxsw_sp,
-                                                nh_group->nh_rif->rif_index);
+                                                nhgi->nh_rif->rif_index);
                if (err)
                        return err;
                trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP;
@@ -4371,19 +4598,20 @@ static int mlxsw_sp_fib_entry_op_remote(struct mlxsw_sp *mlxsw_sp,
                trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
        }
 
-       mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
-       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);
+       mlxsw_sp_fib_entry_pack(op_ctx, fib_entry, op);
+       ll_ops->fib_entry_act_remote_pack(op_ctx, trap_action, trap_id,
+                                         adjacency_index, ecmp_size);
+       return mlxsw_sp_fib_entry_commit(mlxsw_sp, op_ctx, ll_ops);
 }
 
 static int mlxsw_sp_fib_entry_op_local(struct mlxsw_sp *mlxsw_sp,
+                                      struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
                                       struct mlxsw_sp_fib_entry *fib_entry,
-                                      enum mlxsw_reg_ralue_op op)
+                                      enum mlxsw_sp_fib_entry_op op)
 {
-       struct mlxsw_sp_rif *rif = fib_entry->nh_group->nh_rif;
+       const struct mlxsw_sp_router_ll_ops *ll_ops = fib_entry->fib_node->fib->ll_ops;
+       struct mlxsw_sp_rif *rif = fib_entry->nh_group->nhgi->nh_rif;
        enum mlxsw_reg_ralue_trap_action trap_action;
-       char ralue_pl[MLXSW_REG_RALUE_LEN];
        u16 trap_id = 0;
        u16 rif_index = 0;
 
@@ -4395,58 +4623,62 @@ static int mlxsw_sp_fib_entry_op_local(struct mlxsw_sp *mlxsw_sp,
                trap_id = MLXSW_TRAP_ID_RTR_INGRESS0;
        }
 
-       mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
-       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);
+       mlxsw_sp_fib_entry_pack(op_ctx, fib_entry, op);
+       ll_ops->fib_entry_act_local_pack(op_ctx, trap_action, trap_id, rif_index);
+       return mlxsw_sp_fib_entry_commit(mlxsw_sp, op_ctx, ll_ops);
 }
 
 static int mlxsw_sp_fib_entry_op_trap(struct mlxsw_sp *mlxsw_sp,
+                                     struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
                                      struct mlxsw_sp_fib_entry *fib_entry,
-                                     enum mlxsw_reg_ralue_op op)
+                                     enum mlxsw_sp_fib_entry_op op)
 {
-       char ralue_pl[MLXSW_REG_RALUE_LEN];
+       const struct mlxsw_sp_router_ll_ops *ll_ops = fib_entry->fib_node->fib->ll_ops;
 
-       mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
-       mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
-       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
+       mlxsw_sp_fib_entry_pack(op_ctx, fib_entry, op);
+       ll_ops->fib_entry_act_ip2me_pack(op_ctx);
+       return mlxsw_sp_fib_entry_commit(mlxsw_sp, op_ctx, ll_ops);
 }
 
 static int mlxsw_sp_fib_entry_op_blackhole(struct mlxsw_sp *mlxsw_sp,
+                                          struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
                                           struct mlxsw_sp_fib_entry *fib_entry,
-                                          enum mlxsw_reg_ralue_op op)
+                                          enum mlxsw_sp_fib_entry_op op)
 {
+       const struct mlxsw_sp_router_ll_ops *ll_ops = fib_entry->fib_node->fib->ll_ops;
        enum mlxsw_reg_ralue_trap_action trap_action;
-       char ralue_pl[MLXSW_REG_RALUE_LEN];
 
        trap_action = MLXSW_REG_RALUE_TRAP_ACTION_DISCARD_ERROR;
-       mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
-       mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, 0, 0);
-       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
+       mlxsw_sp_fib_entry_pack(op_ctx, fib_entry, op);
+       ll_ops->fib_entry_act_local_pack(op_ctx, trap_action, 0, 0);
+       return mlxsw_sp_fib_entry_commit(mlxsw_sp, op_ctx, ll_ops);
 }
 
 static int
 mlxsw_sp_fib_entry_op_unreachable(struct mlxsw_sp *mlxsw_sp,
+                                 struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
                                  struct mlxsw_sp_fib_entry *fib_entry,
-                                 enum mlxsw_reg_ralue_op op)
+                                 enum mlxsw_sp_fib_entry_op op)
 {
+       const struct mlxsw_sp_router_ll_ops *ll_ops = fib_entry->fib_node->fib->ll_ops;
        enum mlxsw_reg_ralue_trap_action trap_action;
-       char ralue_pl[MLXSW_REG_RALUE_LEN];
        u16 trap_id;
 
        trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP;
        trap_id = MLXSW_TRAP_ID_RTR_INGRESS1;
 
-       mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
-       mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, trap_id, 0);
-       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
+       mlxsw_sp_fib_entry_pack(op_ctx, fib_entry, op);
+       ll_ops->fib_entry_act_local_pack(op_ctx, trap_action, trap_id, 0);
+       return mlxsw_sp_fib_entry_commit(mlxsw_sp, op_ctx, ll_ops);
 }
 
 static int
 mlxsw_sp_fib_entry_op_ipip_decap(struct mlxsw_sp *mlxsw_sp,
+                                struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
                                 struct mlxsw_sp_fib_entry *fib_entry,
-                                enum mlxsw_reg_ralue_op op)
+                                enum mlxsw_sp_fib_entry_op op)
 {
+       const struct mlxsw_sp_router_ll_ops *ll_ops = fib_entry->fib_node->fib->ll_ops;
        struct mlxsw_sp_ipip_entry *ipip_entry = fib_entry->decap.ipip_entry;
        const struct mlxsw_sp_ipip_ops *ipip_ops;
 
@@ -4454,52 +4686,53 @@ mlxsw_sp_fib_entry_op_ipip_decap(struct mlxsw_sp *mlxsw_sp,
                return -EINVAL;
 
        ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt];
-       return ipip_ops->fib_entry_op(mlxsw_sp, ipip_entry, op,
-                                     fib_entry->decap.tunnel_index);
+       return ipip_ops->fib_entry_op(mlxsw_sp, ll_ops, op_ctx, ipip_entry, op,
+                                     fib_entry->decap.tunnel_index, fib_entry->priv);
 }
 
 static int mlxsw_sp_fib_entry_op_nve_decap(struct mlxsw_sp *mlxsw_sp,
+                                          struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
                                           struct mlxsw_sp_fib_entry *fib_entry,
-                                          enum mlxsw_reg_ralue_op op)
+                                          enum mlxsw_sp_fib_entry_op op)
 {
-       char ralue_pl[MLXSW_REG_RALUE_LEN];
+       const struct mlxsw_sp_router_ll_ops *ll_ops = fib_entry->fib_node->fib->ll_ops;
 
-       mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
-       mlxsw_reg_ralue_act_ip2me_tun_pack(ralue_pl,
-                                          fib_entry->decap.tunnel_index);
-       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
+       mlxsw_sp_fib_entry_pack(op_ctx, fib_entry, op);
+       ll_ops->fib_entry_act_ip2me_tun_pack(op_ctx,
+                                            fib_entry->decap.tunnel_index);
+       return mlxsw_sp_fib_entry_commit(mlxsw_sp, op_ctx, ll_ops);
 }
 
 static int __mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
+                                  struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
                                   struct mlxsw_sp_fib_entry *fib_entry,
-                                  enum mlxsw_reg_ralue_op op)
+                                  enum mlxsw_sp_fib_entry_op op)
 {
        switch (fib_entry->type) {
        case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
-               return mlxsw_sp_fib_entry_op_remote(mlxsw_sp, fib_entry, op);
+               return mlxsw_sp_fib_entry_op_remote(mlxsw_sp, op_ctx, fib_entry, op);
        case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
-               return mlxsw_sp_fib_entry_op_local(mlxsw_sp, fib_entry, op);
+               return mlxsw_sp_fib_entry_op_local(mlxsw_sp, op_ctx, fib_entry, op);
        case MLXSW_SP_FIB_ENTRY_TYPE_TRAP:
-               return mlxsw_sp_fib_entry_op_trap(mlxsw_sp, fib_entry, op);
+               return mlxsw_sp_fib_entry_op_trap(mlxsw_sp, op_ctx, fib_entry, op);
        case MLXSW_SP_FIB_ENTRY_TYPE_BLACKHOLE:
-               return mlxsw_sp_fib_entry_op_blackhole(mlxsw_sp, fib_entry, op);
+               return mlxsw_sp_fib_entry_op_blackhole(mlxsw_sp, op_ctx, fib_entry, op);
        case MLXSW_SP_FIB_ENTRY_TYPE_UNREACHABLE:
-               return mlxsw_sp_fib_entry_op_unreachable(mlxsw_sp, fib_entry,
-                                                        op);
+               return mlxsw_sp_fib_entry_op_unreachable(mlxsw_sp, op_ctx, fib_entry, op);
        case MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP:
-               return mlxsw_sp_fib_entry_op_ipip_decap(mlxsw_sp,
-                                                       fib_entry, op);
+               return mlxsw_sp_fib_entry_op_ipip_decap(mlxsw_sp, op_ctx, fib_entry, op);
        case MLXSW_SP_FIB_ENTRY_TYPE_NVE_DECAP:
-               return mlxsw_sp_fib_entry_op_nve_decap(mlxsw_sp, fib_entry, op);
+               return mlxsw_sp_fib_entry_op_nve_decap(mlxsw_sp, op_ctx, fib_entry, op);
        }
        return -EINVAL;
 }
 
 static int mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
+                                struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
                                 struct mlxsw_sp_fib_entry *fib_entry,
-                                enum mlxsw_reg_ralue_op op)
+                                enum mlxsw_sp_fib_entry_op op)
 {
-       int err = __mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry, op);
+       int err = __mlxsw_sp_fib_entry_op(mlxsw_sp, op_ctx, fib_entry, op);
 
        if (err)
                return err;
@@ -4509,18 +4742,35 @@ static int mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
        return err;
 }
 
+static int __mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
+                                      struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
+                                      struct mlxsw_sp_fib_entry *fib_entry,
+                                      bool is_new)
+{
+       return mlxsw_sp_fib_entry_op(mlxsw_sp, op_ctx, fib_entry,
+                                    is_new ? MLXSW_SP_FIB_ENTRY_OP_WRITE :
+                                             MLXSW_SP_FIB_ENTRY_OP_UPDATE);
+}
+
 static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
                                     struct mlxsw_sp_fib_entry *fib_entry)
 {
-       return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
-                                    MLXSW_REG_RALUE_OP_WRITE_WRITE);
+       struct mlxsw_sp_fib_entry_op_ctx *op_ctx = mlxsw_sp->router->ll_op_ctx;
+
+       mlxsw_sp_fib_entry_op_ctx_clear(op_ctx);
+       return __mlxsw_sp_fib_entry_update(mlxsw_sp, op_ctx, fib_entry, false);
 }
 
 static int mlxsw_sp_fib_entry_del(struct mlxsw_sp *mlxsw_sp,
+                                 struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
                                  struct mlxsw_sp_fib_entry *fib_entry)
 {
-       return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
-                                    MLXSW_REG_RALUE_OP_WRITE_DELETE);
+       const struct mlxsw_sp_router_ll_ops *ll_ops = fib_entry->fib_node->fib->ll_ops;
+
+       if (!ll_ops->fib_entry_is_committed(fib_entry->priv))
+               return 0;
+       return mlxsw_sp_fib_entry_op(mlxsw_sp, op_ctx, fib_entry,
+                                    MLXSW_SP_FIB_ENTRY_OP_DELETE);
 }
 
 static int
@@ -4537,8 +4787,8 @@ mlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp,
 
        switch (fen_info->type) {
        case RTN_LOCAL:
-               ipip_entry = mlxsw_sp_ipip_entry_find_by_decap(mlxsw_sp, dev,
-                                                MLXSW_SP_L3_PROTO_IPV4, dip);
+               ipip_entry = mlxsw_sp_ipip_entry_find_by_decap(mlxsw_sp, dev->ifindex,
+                                                              MLXSW_SP_L3_PROTO_IPV4, dip);
                if (ipip_entry && ipip_entry->ol_dev->flags & IFF_UP) {
                        fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP;
                        return mlxsw_sp_fib_entry_decap_init(mlxsw_sp,
@@ -4608,6 +4858,12 @@ mlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp,
                return ERR_PTR(-ENOMEM);
        fib_entry = &fib4_entry->common;
 
+       fib_entry->priv = mlxsw_sp_fib_entry_priv_create(fib_node->fib->ll_ops);
+       if (IS_ERR(fib_entry->priv)) {
+               err = PTR_ERR(fib_entry->priv);
+               goto err_fib_entry_priv_create;
+       }
+
        err = mlxsw_sp_fib4_entry_type_set(mlxsw_sp, fen_info, fib_entry);
        if (err)
                goto err_fib4_entry_type_set;
@@ -4616,7 +4872,8 @@ mlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp,
        if (err)
                goto err_nexthop4_group_get;
 
-       fib4_entry->prio = fen_info->fi->fib_priority;
+       fib4_entry->fi = fen_info->fi;
+       fib_info_hold(fib4_entry->fi);
        fib4_entry->tb_id = fen_info->tb_id;
        fib4_entry->type = fen_info->type;
        fib4_entry->tos = fen_info->tos;
@@ -4628,6 +4885,8 @@ mlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp,
 err_nexthop4_group_get:
        mlxsw_sp_fib4_entry_type_unset(mlxsw_sp, fib_entry);
 err_fib4_entry_type_set:
+       mlxsw_sp_fib_entry_priv_put(fib_entry->priv);
+err_fib_entry_priv_create:
        kfree(fib4_entry);
        return ERR_PTR(err);
 }
@@ -4635,8 +4894,10 @@ err_fib4_entry_type_set:
 static void mlxsw_sp_fib4_entry_destroy(struct mlxsw_sp *mlxsw_sp,
                                        struct mlxsw_sp_fib4_entry *fib4_entry)
 {
+       fib_info_put(fib4_entry->fi);
        mlxsw_sp_nexthop4_group_put(mlxsw_sp, &fib4_entry->common);
        mlxsw_sp_fib4_entry_type_unset(mlxsw_sp, &fib4_entry->common);
+       mlxsw_sp_fib_entry_priv_put(fib4_entry->common.priv);
        kfree(fib4_entry);
 }
 
@@ -4665,8 +4926,7 @@ mlxsw_sp_fib4_entry_lookup(struct mlxsw_sp *mlxsw_sp,
        if (fib4_entry->tb_id == fen_info->tb_id &&
            fib4_entry->tos == fen_info->tos &&
            fib4_entry->type == fen_info->type &&
-           mlxsw_sp_nexthop4_group_fi(fib4_entry->common.nh_group) ==
-           fen_info->fi)
+           fib4_entry->fi == fen_info->fi)
                return fib4_entry;
 
        return NULL;
@@ -4875,14 +5135,16 @@ static void mlxsw_sp_fib_node_put(struct mlxsw_sp *mlxsw_sp,
 }
 
 static int mlxsw_sp_fib_node_entry_link(struct mlxsw_sp *mlxsw_sp,
+                                       struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
                                        struct mlxsw_sp_fib_entry *fib_entry)
 {
        struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
+       bool is_new = !fib_node->fib_entry;
        int err;
 
        fib_node->fib_entry = fib_entry;
 
-       err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
+       err = __mlxsw_sp_fib_entry_update(mlxsw_sp, op_ctx, fib_entry, is_new);
        if (err)
                goto err_fib_entry_update;
 
@@ -4893,14 +5155,25 @@ err_fib_entry_update:
        return err;
 }
 
-static void
-mlxsw_sp_fib_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
-                              struct mlxsw_sp_fib_entry *fib_entry)
+static int __mlxsw_sp_fib_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
+                                           struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
+                                           struct mlxsw_sp_fib_entry *fib_entry)
 {
        struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node;
+       int err;
 
-       mlxsw_sp_fib_entry_del(mlxsw_sp, fib_entry);
+       err = mlxsw_sp_fib_entry_del(mlxsw_sp, op_ctx, fib_entry);
        fib_node->fib_entry = NULL;
+       return err;
+}
+
+static void mlxsw_sp_fib_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
+                                          struct mlxsw_sp_fib_entry *fib_entry)
+{
+       struct mlxsw_sp_fib_entry_op_ctx *op_ctx = mlxsw_sp->router->ll_op_ctx;
+
+       mlxsw_sp_fib_entry_op_ctx_clear(op_ctx);
+       __mlxsw_sp_fib_node_entry_unlink(mlxsw_sp, op_ctx, fib_entry);
 }
 
 static bool mlxsw_sp_fib4_allow_replace(struct mlxsw_sp_fib4_entry *fib4_entry)
@@ -4922,6 +5195,7 @@ static bool mlxsw_sp_fib4_allow_replace(struct mlxsw_sp_fib4_entry *fib4_entry)
 
 static int
 mlxsw_sp_router_fib4_replace(struct mlxsw_sp *mlxsw_sp,
+                            struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
                             const struct fib_entry_notifier_info *fen_info)
 {
        struct mlxsw_sp_fib4_entry *fib4_entry, *fib4_replaced;
@@ -4955,7 +5229,7 @@ mlxsw_sp_router_fib4_replace(struct mlxsw_sp *mlxsw_sp,
        }
 
        replaced = fib_node->fib_entry;
-       err = mlxsw_sp_fib_node_entry_link(mlxsw_sp, &fib4_entry->common);
+       err = mlxsw_sp_fib_node_entry_link(mlxsw_sp, op_ctx, &fib4_entry->common);
        if (err) {
                dev_warn(mlxsw_sp->bus_info->dev, "Failed to link FIB entry to node\n");
                goto err_fib_node_entry_link;
@@ -4980,23 +5254,26 @@ err_fib4_entry_create:
        return err;
 }
 
-static void mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp,
-                                    struct fib_entry_notifier_info *fen_info)
+static int mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp,
+                                   struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
+                                   struct fib_entry_notifier_info *fen_info)
 {
        struct mlxsw_sp_fib4_entry *fib4_entry;
        struct mlxsw_sp_fib_node *fib_node;
+       int err;
 
        if (mlxsw_sp->router->aborted)
-               return;
+               return 0;
 
        fib4_entry = mlxsw_sp_fib4_entry_lookup(mlxsw_sp, fen_info);
        if (!fib4_entry)
-               return;
+               return 0;
        fib_node = fib4_entry->common.fib_node;
 
-       mlxsw_sp_fib_node_entry_unlink(mlxsw_sp, &fib4_entry->common);
+       err = __mlxsw_sp_fib_node_entry_unlink(mlxsw_sp, op_ctx, &fib4_entry->common);
        mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
        mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
+       return err;
 }
 
 static bool mlxsw_sp_fib6_rt_should_ignore(const struct fib6_info *rt)
@@ -5081,51 +5358,6 @@ static bool mlxsw_sp_nexthop6_ipip_type(const struct mlxsw_sp *mlxsw_sp,
               mlxsw_sp_netdev_ipip_type(mlxsw_sp, rt->fib6_nh->fib_nh_dev, ret);
 }
 
-static int mlxsw_sp_nexthop6_type_init(struct mlxsw_sp *mlxsw_sp,
-                                      struct mlxsw_sp_nexthop_group *nh_grp,
-                                      struct mlxsw_sp_nexthop *nh,
-                                      const struct fib6_info *rt)
-{
-       const struct mlxsw_sp_ipip_ops *ipip_ops;
-       struct mlxsw_sp_ipip_entry *ipip_entry;
-       struct net_device *dev = rt->fib6_nh->fib_nh_dev;
-       struct mlxsw_sp_rif *rif;
-       int err;
-
-       ipip_entry = mlxsw_sp_ipip_entry_find_by_ol_dev(mlxsw_sp, dev);
-       if (ipip_entry) {
-               ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt];
-               if (ipip_ops->can_offload(mlxsw_sp, dev,
-                                         MLXSW_SP_L3_PROTO_IPV6)) {
-                       nh->type = MLXSW_SP_NEXTHOP_TYPE_IPIP;
-                       mlxsw_sp_nexthop_ipip_init(mlxsw_sp, nh, ipip_entry);
-                       return 0;
-               }
-       }
-
-       nh->type = MLXSW_SP_NEXTHOP_TYPE_ETH;
-       rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
-       if (!rif)
-               return 0;
-       mlxsw_sp_nexthop_rif_init(nh, rif);
-
-       err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
-       if (err)
-               goto err_nexthop_neigh_init;
-
-       return 0;
-
-err_nexthop_neigh_init:
-       mlxsw_sp_nexthop_rif_fini(nh);
-       return err;
-}
-
-static void mlxsw_sp_nexthop6_type_fini(struct mlxsw_sp *mlxsw_sp,
-                                       struct mlxsw_sp_nexthop *nh)
-{
-       mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);
-}
-
 static int mlxsw_sp_nexthop6_init(struct mlxsw_sp *mlxsw_sp,
                                  struct mlxsw_sp_nexthop_group *nh_grp,
                                  struct mlxsw_sp_nexthop *nh,
@@ -5133,9 +5365,12 @@ static int mlxsw_sp_nexthop6_init(struct mlxsw_sp *mlxsw_sp,
 {
        struct net_device *dev = rt->fib6_nh->fib_nh_dev;
 
-       nh->nh_grp = nh_grp;
+       nh->nhgi = nh_grp->nhgi;
        nh->nh_weight = rt->fib6_nh->fib_nh_weight;
        memcpy(&nh->gw_addr, &rt->fib6_nh->fib_nh_gw6, sizeof(nh->gw_addr));
+#if IS_ENABLED(CONFIG_IPV6)
+       nh->neigh_tbl = &nd_tbl;
+#endif
        mlxsw_sp_nexthop_counter_alloc(mlxsw_sp, nh);
 
        list_add_tail(&nh->router_list_node, &mlxsw_sp->router->nexthop_list);
@@ -5144,13 +5379,13 @@ static int mlxsw_sp_nexthop6_init(struct mlxsw_sp *mlxsw_sp,
                return 0;
        nh->ifindex = dev->ifindex;
 
-       return mlxsw_sp_nexthop6_type_init(mlxsw_sp, nh_grp, nh, rt);
+       return mlxsw_sp_nexthop_type_init(mlxsw_sp, nh, dev);
 }
 
 static void mlxsw_sp_nexthop6_fini(struct mlxsw_sp *mlxsw_sp,
                                   struct mlxsw_sp_nexthop *nh)
 {
-       mlxsw_sp_nexthop6_type_fini(mlxsw_sp, nh);
+       mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);
        list_del(&nh->router_list_node);
        mlxsw_sp_nexthop_counter_free(mlxsw_sp, nh);
 }
@@ -5162,51 +5397,92 @@ static bool mlxsw_sp_rt6_is_gateway(const struct mlxsw_sp *mlxsw_sp,
               mlxsw_sp_nexthop6_ipip_type(mlxsw_sp, rt, NULL);
 }
 
-static struct mlxsw_sp_nexthop_group *
-mlxsw_sp_nexthop6_group_create(struct mlxsw_sp *mlxsw_sp,
-                              struct mlxsw_sp_fib6_entry *fib6_entry)
+static int
+mlxsw_sp_nexthop6_group_info_init(struct mlxsw_sp *mlxsw_sp,
+                                 struct mlxsw_sp_nexthop_group *nh_grp,
+                                 struct mlxsw_sp_fib6_entry *fib6_entry)
 {
-       struct mlxsw_sp_nexthop_group *nh_grp;
+       struct mlxsw_sp_nexthop_group_info *nhgi;
        struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
        struct mlxsw_sp_nexthop *nh;
-       int i = 0;
-       int err;
+       int err, i;
 
-       nh_grp = kzalloc(struct_size(nh_grp, nexthops, fib6_entry->nrt6),
-                        GFP_KERNEL);
-       if (!nh_grp)
-               return ERR_PTR(-ENOMEM);
-       INIT_LIST_HEAD(&nh_grp->fib_list);
-#if IS_ENABLED(CONFIG_IPV6)
-       nh_grp->neigh_tbl = &nd_tbl;
-#endif
+       nhgi = kzalloc(struct_size(nhgi, nexthops, fib6_entry->nrt6),
+                      GFP_KERNEL);
+       if (!nhgi)
+               return -ENOMEM;
+       nh_grp->nhgi = nhgi;
+       nhgi->nh_grp = nh_grp;
        mlxsw_sp_rt6 = list_first_entry(&fib6_entry->rt6_list,
                                        struct mlxsw_sp_rt6, list);
-       nh_grp->gateway = mlxsw_sp_rt6_is_gateway(mlxsw_sp, mlxsw_sp_rt6->rt);
-       nh_grp->count = fib6_entry->nrt6;
-       for (i = 0; i < nh_grp->count; i++) {
+       nhgi->gateway = mlxsw_sp_rt6_is_gateway(mlxsw_sp, mlxsw_sp_rt6->rt);
+       nhgi->count = fib6_entry->nrt6;
+       for (i = 0; i < nhgi->count; i++) {
                struct fib6_info *rt = mlxsw_sp_rt6->rt;
 
-               nh = &nh_grp->nexthops[i];
+               nh = &nhgi->nexthops[i];
                err = mlxsw_sp_nexthop6_init(mlxsw_sp, nh_grp, nh, rt);
                if (err)
                        goto err_nexthop6_init;
                mlxsw_sp_rt6 = list_next_entry(mlxsw_sp_rt6, list);
        }
+       nh_grp->nhgi = nhgi;
+       mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
+
+       return 0;
+
+err_nexthop6_init:
+       for (i--; i >= 0; i--) {
+               nh = &nhgi->nexthops[i];
+               mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
+       }
+       kfree(nhgi);
+       return err;
+}
+
+static void
+mlxsw_sp_nexthop6_group_info_fini(struct mlxsw_sp *mlxsw_sp,
+                                 struct mlxsw_sp_nexthop_group *nh_grp)
+{
+       struct mlxsw_sp_nexthop_group_info *nhgi = nh_grp->nhgi;
+       int i;
+
+       for (i = nhgi->count - 1; i >= 0; i--) {
+               struct mlxsw_sp_nexthop *nh = &nhgi->nexthops[i];
+
+               mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
+       }
+       mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
+       WARN_ON_ONCE(nhgi->adj_index_valid);
+       kfree(nhgi);
+}
+
+static struct mlxsw_sp_nexthop_group *
+mlxsw_sp_nexthop6_group_create(struct mlxsw_sp *mlxsw_sp,
+                              struct mlxsw_sp_fib6_entry *fib6_entry)
+{
+       struct mlxsw_sp_nexthop_group *nh_grp;
+       int err;
+
+       nh_grp = kzalloc(sizeof(*nh_grp), GFP_KERNEL);
+       if (!nh_grp)
+               return ERR_PTR(-ENOMEM);
+       INIT_LIST_HEAD(&nh_grp->fib_list);
+       nh_grp->type = MLXSW_SP_NEXTHOP_GROUP_TYPE_IPV6;
+
+       err = mlxsw_sp_nexthop6_group_info_init(mlxsw_sp, nh_grp, fib6_entry);
+       if (err)
+               goto err_nexthop_group_info_init;
 
        err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp);
        if (err)
                goto err_nexthop_group_insert;
 
-       mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
        return nh_grp;
 
 err_nexthop_group_insert:
-err_nexthop6_init:
-       for (i--; i >= 0; i--) {
-               nh = &nh_grp->nexthops[i];
-               mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
-       }
+       mlxsw_sp_nexthop6_group_info_fini(mlxsw_sp, nh_grp);
+err_nexthop_group_info_init:
        kfree(nh_grp);
        return ERR_PTR(err);
 }
@@ -5215,16 +5491,8 @@ static void
 mlxsw_sp_nexthop6_group_destroy(struct mlxsw_sp *mlxsw_sp,
                                struct mlxsw_sp_nexthop_group *nh_grp)
 {
-       struct mlxsw_sp_nexthop *nh;
-       int i = nh_grp->count;
-
        mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp);
-       for (i--; i >= 0; i--) {
-               nh = &nh_grp->nexthops[i];
-               mlxsw_sp_nexthop6_fini(mlxsw_sp, nh);
-       }
-       mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
-       WARN_ON(nh_grp->adj_index_valid);
+       mlxsw_sp_nexthop6_group_info_fini(mlxsw_sp, nh_grp);
        kfree(nh_grp);
 }
 
@@ -5263,9 +5531,9 @@ static void mlxsw_sp_nexthop6_group_put(struct mlxsw_sp *mlxsw_sp,
        mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, nh_grp);
 }
 
-static int
-mlxsw_sp_nexthop6_group_update(struct mlxsw_sp *mlxsw_sp,
-                              struct mlxsw_sp_fib6_entry *fib6_entry)
+static int mlxsw_sp_nexthop6_group_update(struct mlxsw_sp *mlxsw_sp,
+                                         struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
+                                         struct mlxsw_sp_fib6_entry *fib6_entry)
 {
        struct mlxsw_sp_nexthop_group *old_nh_grp = fib6_entry->common.nh_group;
        int err;
@@ -5281,7 +5549,8 @@ mlxsw_sp_nexthop6_group_update(struct mlxsw_sp *mlxsw_sp,
         * currently associated with it in the device's table is that
         * of the old group. Start using the new one instead.
         */
-       err = mlxsw_sp_fib_entry_update(mlxsw_sp, &fib6_entry->common);
+       err = __mlxsw_sp_fib_entry_update(mlxsw_sp, op_ctx,
+                                         &fib6_entry->common, false);
        if (err)
                goto err_fib_entry_update;
 
@@ -5301,6 +5570,7 @@ err_nexthop6_group_get:
 
 static int
 mlxsw_sp_fib6_entry_nexthop_add(struct mlxsw_sp *mlxsw_sp,
+                               struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
                                struct mlxsw_sp_fib6_entry *fib6_entry,
                                struct fib6_info **rt_arr, unsigned int nrt6)
 {
@@ -5318,7 +5588,7 @@ mlxsw_sp_fib6_entry_nexthop_add(struct mlxsw_sp *mlxsw_sp,
                fib6_entry->nrt6++;
        }
 
-       err = mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);
+       err = mlxsw_sp_nexthop6_group_update(mlxsw_sp, op_ctx, fib6_entry);
        if (err)
                goto err_nexthop6_group_update;
 
@@ -5339,6 +5609,7 @@ err_rt6_create:
 
 static void
 mlxsw_sp_fib6_entry_nexthop_del(struct mlxsw_sp *mlxsw_sp,
+                               struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
                                struct mlxsw_sp_fib6_entry *fib6_entry,
                                struct fib6_info **rt_arr, unsigned int nrt6)
 {
@@ -5356,19 +5627,13 @@ mlxsw_sp_fib6_entry_nexthop_del(struct mlxsw_sp *mlxsw_sp,
                mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
        }
 
-       mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry);
+       mlxsw_sp_nexthop6_group_update(mlxsw_sp, op_ctx, fib6_entry);
 }
 
 static void mlxsw_sp_fib6_entry_type_set(struct mlxsw_sp *mlxsw_sp,
                                         struct mlxsw_sp_fib_entry *fib_entry,
                                         const struct fib6_info *rt)
 {
-       /* Packets hitting RTF_REJECT routes need to be discarded by the
-        * stack. We can rely on their destination device not having a
-        * RIF (it's the loopback device) and can thus use action type
-        * local, which will cause them to be trapped with a lower
-        * priority than packets that need to be locally received.
-        */
        if (rt->fib6_flags & (RTF_LOCAL | RTF_ANYCAST))
                fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
        else if (rt->fib6_type == RTN_BLACKHOLE)
@@ -5409,6 +5674,12 @@ mlxsw_sp_fib6_entry_create(struct mlxsw_sp *mlxsw_sp,
                return ERR_PTR(-ENOMEM);
        fib_entry = &fib6_entry->common;
 
+       fib_entry->priv = mlxsw_sp_fib_entry_priv_create(fib_node->fib->ll_ops);
+       if (IS_ERR(fib_entry->priv)) {
+               err = PTR_ERR(fib_entry->priv);
+               goto err_fib_entry_priv_create;
+       }
+
        INIT_LIST_HEAD(&fib6_entry->rt6_list);
 
        for (i = 0; i < nrt6; i++) {
@@ -5441,6 +5712,8 @@ err_rt6_create:
                list_del(&mlxsw_sp_rt6->list);
                mlxsw_sp_rt6_destroy(mlxsw_sp_rt6);
        }
+       mlxsw_sp_fib_entry_priv_put(fib_entry->priv);
+err_fib_entry_priv_create:
        kfree(fib6_entry);
        return ERR_PTR(err);
 }
@@ -5451,6 +5724,7 @@ static void mlxsw_sp_fib6_entry_destroy(struct mlxsw_sp *mlxsw_sp,
        mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common);
        mlxsw_sp_fib6_entry_rt_destroy_all(fib6_entry);
        WARN_ON(fib6_entry->nrt6);
+       mlxsw_sp_fib_entry_priv_put(fib6_entry->common.priv);
        kfree(fib6_entry);
 }
 
@@ -5508,8 +5782,8 @@ static bool mlxsw_sp_fib6_allow_replace(struct mlxsw_sp_fib6_entry *fib6_entry)
 }
 
 static int mlxsw_sp_router_fib6_replace(struct mlxsw_sp *mlxsw_sp,
-                                       struct fib6_info **rt_arr,
-                                       unsigned int nrt6)
+                                       struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
+                                       struct fib6_info **rt_arr, unsigned int nrt6)
 {
        struct mlxsw_sp_fib6_entry *fib6_entry, *fib6_replaced;
        struct mlxsw_sp_fib_entry *replaced;
@@ -5548,7 +5822,7 @@ static int mlxsw_sp_router_fib6_replace(struct mlxsw_sp *mlxsw_sp,
        }
 
        replaced = fib_node->fib_entry;
-       err = mlxsw_sp_fib_node_entry_link(mlxsw_sp, &fib6_entry->common);
+       err = mlxsw_sp_fib_node_entry_link(mlxsw_sp, op_ctx, &fib6_entry->common);
        if (err)
                goto err_fib_node_entry_link;
 
@@ -5572,8 +5846,8 @@ err_fib6_entry_create:
 }
 
 static int mlxsw_sp_router_fib6_append(struct mlxsw_sp *mlxsw_sp,
-                                      struct fib6_info **rt_arr,
-                                      unsigned int nrt6)
+                                      struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
+                                      struct fib6_info **rt_arr, unsigned int nrt6)
 {
        struct mlxsw_sp_fib6_entry *fib6_entry;
        struct mlxsw_sp_fib_node *fib_node;
@@ -5604,8 +5878,7 @@ static int mlxsw_sp_router_fib6_append(struct mlxsw_sp *mlxsw_sp,
 
        fib6_entry = container_of(fib_node->fib_entry,
                                  struct mlxsw_sp_fib6_entry, common);
-       err = mlxsw_sp_fib6_entry_nexthop_add(mlxsw_sp, fib6_entry, rt_arr,
-                                             nrt6);
+       err = mlxsw_sp_fib6_entry_nexthop_add(mlxsw_sp, op_ctx, fib6_entry, rt_arr, nrt6);
        if (err)
                goto err_fib6_entry_nexthop_add;
 
@@ -5616,19 +5889,20 @@ err_fib6_entry_nexthop_add:
        return err;
 }
 
-static void mlxsw_sp_router_fib6_del(struct mlxsw_sp *mlxsw_sp,
-                                    struct fib6_info **rt_arr,
-                                    unsigned int nrt6)
+static int mlxsw_sp_router_fib6_del(struct mlxsw_sp *mlxsw_sp,
+                                   struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
+                                   struct fib6_info **rt_arr, unsigned int nrt6)
 {
        struct mlxsw_sp_fib6_entry *fib6_entry;
        struct mlxsw_sp_fib_node *fib_node;
        struct fib6_info *rt = rt_arr[0];
+       int err;
 
        if (mlxsw_sp->router->aborted)
-               return;
+               return 0;
 
        if (mlxsw_sp_fib6_rt_should_ignore(rt))
-               return;
+               return 0;
 
        /* Multipath routes are first added to the FIB trie and only then
         * notified. If we vetoed the addition, we will get a delete
@@ -5637,58 +5911,66 @@ static void mlxsw_sp_router_fib6_del(struct mlxsw_sp *mlxsw_sp,
         */
        fib6_entry = mlxsw_sp_fib6_entry_lookup(mlxsw_sp, rt);
        if (!fib6_entry)
-               return;
+               return 0;
 
        /* If not all the nexthops are deleted, then only reduce the nexthop
         * group.
         */
        if (nrt6 != fib6_entry->nrt6) {
-               mlxsw_sp_fib6_entry_nexthop_del(mlxsw_sp, fib6_entry, rt_arr,
-                                               nrt6);
-               return;
+               mlxsw_sp_fib6_entry_nexthop_del(mlxsw_sp, op_ctx, fib6_entry, rt_arr, nrt6);
+               return 0;
        }
 
        fib_node = fib6_entry->common.fib_node;
 
-       mlxsw_sp_fib_node_entry_unlink(mlxsw_sp, &fib6_entry->common);
+       err = __mlxsw_sp_fib_node_entry_unlink(mlxsw_sp, op_ctx, &fib6_entry->common);
        mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
        mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
+       return err;
 }
 
 static int __mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp,
-                                           enum mlxsw_reg_ralxx_protocol proto,
+                                           enum mlxsw_sp_l3proto proto,
                                            u8 tree_id)
 {
-       char ralta_pl[MLXSW_REG_RALTA_LEN];
-       char ralst_pl[MLXSW_REG_RALST_LEN];
+       const struct mlxsw_sp_router_ll_ops *ll_ops = mlxsw_sp->router->proto_ll_ops[proto];
+       enum mlxsw_reg_ralxx_protocol ralxx_proto =
+                               (enum mlxsw_reg_ralxx_protocol) proto;
+       struct mlxsw_sp_fib_entry_priv *priv;
+       char xralta_pl[MLXSW_REG_XRALTA_LEN];
+       char xralst_pl[MLXSW_REG_XRALST_LEN];
        int i, err;
 
-       mlxsw_reg_ralta_pack(ralta_pl, true, proto, tree_id);
-       err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
+       mlxsw_reg_xralta_pack(xralta_pl, true, ralxx_proto, tree_id);
+       err = ll_ops->ralta_write(mlxsw_sp, xralta_pl);
        if (err)
                return err;
 
-       mlxsw_reg_ralst_pack(ralst_pl, 0xff, tree_id);
-       err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
+       mlxsw_reg_xralst_pack(xralst_pl, 0xff, tree_id);
+       err = ll_ops->ralst_write(mlxsw_sp, xralst_pl);
        if (err)
                return err;
 
        for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
+               struct mlxsw_sp_fib_entry_op_ctx *op_ctx = mlxsw_sp->router->ll_op_ctx;
                struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
-               char raltb_pl[MLXSW_REG_RALTB_LEN];
-               char ralue_pl[MLXSW_REG_RALUE_LEN];
+               char xraltb_pl[MLXSW_REG_XRALTB_LEN];
 
-               mlxsw_reg_raltb_pack(raltb_pl, vr->id, proto, tree_id);
-               err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb),
-                                     raltb_pl);
+               mlxsw_sp_fib_entry_op_ctx_clear(op_ctx);
+               mlxsw_reg_xraltb_pack(xraltb_pl, vr->id, ralxx_proto, tree_id);
+               err = ll_ops->raltb_write(mlxsw_sp, xraltb_pl);
                if (err)
                        return err;
 
-               mlxsw_reg_ralue_pack(ralue_pl, proto,
-                                    MLXSW_REG_RALUE_OP_WRITE_WRITE, vr->id, 0);
-               mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
-               err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue),
-                                     ralue_pl);
+               priv = mlxsw_sp_fib_entry_priv_create(ll_ops);
+               if (IS_ERR(priv))
+                       return PTR_ERR(priv);
+
+               ll_ops->fib_entry_pack(op_ctx, proto, MLXSW_SP_FIB_ENTRY_OP_WRITE,
+                                      vr->id, 0, NULL, priv);
+               ll_ops->fib_entry_act_ip2me_pack(op_ctx);
+               err = ll_ops->fib_entry_commit(mlxsw_sp, op_ctx, NULL);
+               mlxsw_sp_fib_entry_priv_put(priv);
                if (err)
                        return err;
        }
@@ -5784,7 +6066,7 @@ mlxsw_sp_router_fibmr_vif_del(struct mlxsw_sp *mlxsw_sp,
 
 static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp)
 {
-       enum mlxsw_reg_ralxx_protocol proto = MLXSW_REG_RALXX_PROTOCOL_IPV4;
+       enum mlxsw_sp_l3proto proto = MLXSW_SP_L3_PROTO_IPV4;
        int err;
 
        err = __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
@@ -5796,7 +6078,7 @@ static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp)
         * packets that don't match any routes are trapped to the CPU.
         */
 
-       proto = MLXSW_REG_RALXX_PROTOCOL_IPV6;
+       proto = MLXSW_SP_L3_PROTO_IPV6;
        return __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto,
                                                MLXSW_SP_LPM_TREE_MIN + 1);
 }
@@ -5901,15 +6183,15 @@ static void mlxsw_sp_router_fib_abort(struct mlxsw_sp *mlxsw_sp)
                dev_warn(mlxsw_sp->bus_info->dev, "Failed to set abort trap.\n");
 }
 
-struct mlxsw_sp_fib6_event_work {
+struct mlxsw_sp_fib6_event {
        struct fib6_info **rt_arr;
        unsigned int nrt6;
 };
 
-struct mlxsw_sp_fib_event_work {
-       struct work_struct work;
+struct mlxsw_sp_fib_event {
+       struct list_head list; /* node in fib queue */
        union {
-               struct mlxsw_sp_fib6_event_work fib6_work;
+               struct mlxsw_sp_fib6_event fib6_event;
                struct fib_entry_notifier_info fen_info;
                struct fib_rule_notifier_info fr_info;
                struct fib_nh_notifier_info fnh_info;
@@ -5918,11 +6200,12 @@ struct mlxsw_sp_fib_event_work {
        };
        struct mlxsw_sp *mlxsw_sp;
        unsigned long event;
+       int family;
 };
 
 static int
-mlxsw_sp_router_fib6_work_init(struct mlxsw_sp_fib6_event_work *fib6_work,
-                              struct fib6_entry_notifier_info *fen6_info)
+mlxsw_sp_router_fib6_event_init(struct mlxsw_sp_fib6_event *fib6_event,
+                               struct fib6_entry_notifier_info *fen6_info)
 {
        struct fib6_info *rt = fen6_info->rt;
        struct fib6_info **rt_arr;
@@ -5936,8 +6219,8 @@ mlxsw_sp_router_fib6_work_init(struct mlxsw_sp_fib6_event_work *fib6_work,
        if (!rt_arr)
                return -ENOMEM;
 
-       fib6_work->rt_arr = rt_arr;
-       fib6_work->nrt6 = nrt6;
+       fib6_event->rt_arr = rt_arr;
+       fib6_event->nrt6 = nrt6;
 
        rt_arr[0] = rt;
        fib6_info_hold(rt);
@@ -5959,170 +6242,232 @@ mlxsw_sp_router_fib6_work_init(struct mlxsw_sp_fib6_event_work *fib6_work,
 }
 
 static void
-mlxsw_sp_router_fib6_work_fini(struct mlxsw_sp_fib6_event_work *fib6_work)
+mlxsw_sp_router_fib6_event_fini(struct mlxsw_sp_fib6_event *fib6_event)
 {
        int i;
 
-       for (i = 0; i < fib6_work->nrt6; i++)
-               mlxsw_sp_rt6_release(fib6_work->rt_arr[i]);
-       kfree(fib6_work->rt_arr);
+       for (i = 0; i < fib6_event->nrt6; i++)
+               mlxsw_sp_rt6_release(fib6_event->rt_arr[i]);
+       kfree(fib6_event->rt_arr);
 }
 
-static void mlxsw_sp_router_fib4_event_work(struct work_struct *work)
+static void mlxsw_sp_router_fib4_event_process(struct mlxsw_sp *mlxsw_sp,
+                                              struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
+                                              struct mlxsw_sp_fib_event *fib_event)
 {
-       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;
        int err;
 
-       mutex_lock(&mlxsw_sp->router->lock);
        mlxsw_sp_span_respin(mlxsw_sp);
 
-       switch (fib_work->event) {
+       switch (fib_event->event) {
        case FIB_EVENT_ENTRY_REPLACE:
-               err = mlxsw_sp_router_fib4_replace(mlxsw_sp,
-                                                  &fib_work->fen_info);
-               if (err)
+               err = mlxsw_sp_router_fib4_replace(mlxsw_sp, op_ctx, &fib_event->fen_info);
+               if (err) {
+                       mlxsw_sp_fib_entry_op_ctx_priv_put_all(op_ctx);
                        mlxsw_sp_router_fib_abort(mlxsw_sp);
-               fib_info_put(fib_work->fen_info.fi);
+               }
+               fib_info_put(fib_event->fen_info.fi);
                break;
        case FIB_EVENT_ENTRY_DEL:
-               mlxsw_sp_router_fib4_del(mlxsw_sp, &fib_work->fen_info);
-               fib_info_put(fib_work->fen_info.fi);
+               err = mlxsw_sp_router_fib4_del(mlxsw_sp, op_ctx, &fib_event->fen_info);
+               if (err)
+                       mlxsw_sp_fib_entry_op_ctx_priv_put_all(op_ctx);
+               fib_info_put(fib_event->fen_info.fi);
                break;
        case FIB_EVENT_NH_ADD:
        case FIB_EVENT_NH_DEL:
-               mlxsw_sp_nexthop4_event(mlxsw_sp, fib_work->event,
-                                       fib_work->fnh_info.fib_nh);
-               fib_info_put(fib_work->fnh_info.fib_nh->nh_parent);
+               mlxsw_sp_nexthop4_event(mlxsw_sp, fib_event->event, fib_event->fnh_info.fib_nh);
+               fib_info_put(fib_event->fnh_info.fib_nh->nh_parent);
                break;
        }
-       mutex_unlock(&mlxsw_sp->router->lock);
-       kfree(fib_work);
 }
 
-static void mlxsw_sp_router_fib6_event_work(struct work_struct *work)
+static void mlxsw_sp_router_fib6_event_process(struct mlxsw_sp *mlxsw_sp,
+                                              struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
+                                              struct mlxsw_sp_fib_event *fib_event)
 {
-       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;
        int err;
 
-       mutex_lock(&mlxsw_sp->router->lock);
        mlxsw_sp_span_respin(mlxsw_sp);
 
-       switch (fib_work->event) {
+       switch (fib_event->event) {
        case FIB_EVENT_ENTRY_REPLACE:
-               err = mlxsw_sp_router_fib6_replace(mlxsw_sp,
-                                                  fib_work->fib6_work.rt_arr,
-                                                  fib_work->fib6_work.nrt6);
-               if (err)
+               err = mlxsw_sp_router_fib6_replace(mlxsw_sp, op_ctx, fib_event->fib6_event.rt_arr,
+                                                  fib_event->fib6_event.nrt6);
+               if (err) {
+                       mlxsw_sp_fib_entry_op_ctx_priv_put_all(op_ctx);
                        mlxsw_sp_router_fib_abort(mlxsw_sp);
-               mlxsw_sp_router_fib6_work_fini(&fib_work->fib6_work);
+               }
+               mlxsw_sp_router_fib6_event_fini(&fib_event->fib6_event);
                break;
        case FIB_EVENT_ENTRY_APPEND:
-               err = mlxsw_sp_router_fib6_append(mlxsw_sp,
-                                                 fib_work->fib6_work.rt_arr,
-                                                 fib_work->fib6_work.nrt6);
-               if (err)
+               err = mlxsw_sp_router_fib6_append(mlxsw_sp, op_ctx, fib_event->fib6_event.rt_arr,
+                                                 fib_event->fib6_event.nrt6);
+               if (err) {
+                       mlxsw_sp_fib_entry_op_ctx_priv_put_all(op_ctx);
                        mlxsw_sp_router_fib_abort(mlxsw_sp);
-               mlxsw_sp_router_fib6_work_fini(&fib_work->fib6_work);
+               }
+               mlxsw_sp_router_fib6_event_fini(&fib_event->fib6_event);
                break;
        case FIB_EVENT_ENTRY_DEL:
-               mlxsw_sp_router_fib6_del(mlxsw_sp,
-                                        fib_work->fib6_work.rt_arr,
-                                        fib_work->fib6_work.nrt6);
-               mlxsw_sp_router_fib6_work_fini(&fib_work->fib6_work);
+               err = mlxsw_sp_router_fib6_del(mlxsw_sp, op_ctx, fib_event->fib6_event.rt_arr,
+                                              fib_event->fib6_event.nrt6);
+               if (err)
+                       mlxsw_sp_fib_entry_op_ctx_priv_put_all(op_ctx);
+               mlxsw_sp_router_fib6_event_fini(&fib_event->fib6_event);
                break;
        }
-       mutex_unlock(&mlxsw_sp->router->lock);
-       kfree(fib_work);
 }
 
-static void mlxsw_sp_router_fibmr_event_work(struct work_struct *work)
+static void mlxsw_sp_router_fibmr_event_process(struct mlxsw_sp *mlxsw_sp,
+                                               struct mlxsw_sp_fib_event *fib_event)
 {
-       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;
        int err;
 
        rtnl_lock();
        mutex_lock(&mlxsw_sp->router->lock);
-       switch (fib_work->event) {
+       switch (fib_event->event) {
        case FIB_EVENT_ENTRY_REPLACE:
        case FIB_EVENT_ENTRY_ADD:
-               replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
+               replace = fib_event->event == FIB_EVENT_ENTRY_REPLACE;
 
-               err = mlxsw_sp_router_fibmr_add(mlxsw_sp, &fib_work->men_info,
-                                               replace);
+               err = mlxsw_sp_router_fibmr_add(mlxsw_sp, &fib_event->men_info, replace);
                if (err)
                        mlxsw_sp_router_fib_abort(mlxsw_sp);
-               mr_cache_put(fib_work->men_info.mfc);
+               mr_cache_put(fib_event->men_info.mfc);
                break;
        case FIB_EVENT_ENTRY_DEL:
-               mlxsw_sp_router_fibmr_del(mlxsw_sp, &fib_work->men_info);
-               mr_cache_put(fib_work->men_info.mfc);
+               mlxsw_sp_router_fibmr_del(mlxsw_sp, &fib_event->men_info);
+               mr_cache_put(fib_event->men_info.mfc);
                break;
        case FIB_EVENT_VIF_ADD:
                err = mlxsw_sp_router_fibmr_vif_add(mlxsw_sp,
-                                                   &fib_work->ven_info);
+                                                   &fib_event->ven_info);
                if (err)
                        mlxsw_sp_router_fib_abort(mlxsw_sp);
-               dev_put(fib_work->ven_info.dev);
+               dev_put(fib_event->ven_info.dev);
                break;
        case FIB_EVENT_VIF_DEL:
-               mlxsw_sp_router_fibmr_vif_del(mlxsw_sp,
-                                             &fib_work->ven_info);
-               dev_put(fib_work->ven_info.dev);
+               mlxsw_sp_router_fibmr_vif_del(mlxsw_sp, &fib_event->ven_info);
+               dev_put(fib_event->ven_info.dev);
                break;
        }
        mutex_unlock(&mlxsw_sp->router->lock);
        rtnl_unlock();
-       kfree(fib_work);
 }
 
-static void mlxsw_sp_router_fib4_event(struct mlxsw_sp_fib_event_work *fib_work,
+static void mlxsw_sp_router_fib_event_work(struct work_struct *work)
+{
+       struct mlxsw_sp_router *router = container_of(work, struct mlxsw_sp_router, fib_event_work);
+       struct mlxsw_sp_fib_entry_op_ctx *op_ctx = router->ll_op_ctx;
+       struct mlxsw_sp *mlxsw_sp = router->mlxsw_sp;
+       struct mlxsw_sp_fib_event *next_fib_event;
+       struct mlxsw_sp_fib_event *fib_event;
+       int last_family = AF_UNSPEC;
+       LIST_HEAD(fib_event_queue);
+
+       spin_lock_bh(&router->fib_event_queue_lock);
+       list_splice_init(&router->fib_event_queue, &fib_event_queue);
+       spin_unlock_bh(&router->fib_event_queue_lock);
+
+       /* Router lock is held here to make sure per-instance
+        * operation context is not used in between FIB4/6 events
+        * processing.
+        */
+       mutex_lock(&router->lock);
+       mlxsw_sp_fib_entry_op_ctx_clear(op_ctx);
+       list_for_each_entry_safe(fib_event, next_fib_event,
+                                &fib_event_queue, list) {
+               /* Check if the next entry in the queue exists and it is
+                * of the same type (family and event) as the currect one.
+                * In that case it is permitted to do the bulking
+                * of multiple FIB entries to a single register write.
+                */
+               op_ctx->bulk_ok = !list_is_last(&fib_event->list, &fib_event_queue) &&
+                                 fib_event->family == next_fib_event->family &&
+                                 fib_event->event == next_fib_event->event;
+
+               /* In case family of this and the previous entry are different, context
+                * reinitialization is going to be needed now, indicate that.
+                * Note that since last_family is initialized to AF_UNSPEC, this is always
+                * going to happen for the first entry processed in the work.
+                */
+               if (fib_event->family != last_family)
+                       op_ctx->initialized = false;
+
+               switch (fib_event->family) {
+               case AF_INET:
+                       mlxsw_sp_router_fib4_event_process(mlxsw_sp, op_ctx,
+                                                          fib_event);
+                       break;
+               case AF_INET6:
+                       mlxsw_sp_router_fib6_event_process(mlxsw_sp, op_ctx,
+                                                          fib_event);
+                       break;
+               case RTNL_FAMILY_IP6MR:
+               case RTNL_FAMILY_IPMR:
+                       /* Unlock here as inside FIBMR the lock is taken again
+                        * under RTNL. The per-instance operation context
+                        * is not used by FIBMR.
+                        */
+                       mutex_unlock(&router->lock);
+                       mlxsw_sp_router_fibmr_event_process(mlxsw_sp,
+                                                           fib_event);
+                       mutex_lock(&router->lock);
+                       break;
+               default:
+                       WARN_ON_ONCE(1);
+               }
+               last_family = fib_event->family;
+               kfree(fib_event);
+               cond_resched();
+       }
+       WARN_ON_ONCE(!list_empty(&router->ll_op_ctx->fib_entry_priv_list));
+       mutex_unlock(&router->lock);
+}
+
+static void mlxsw_sp_router_fib4_event(struct mlxsw_sp_fib_event *fib_event,
                                       struct fib_notifier_info *info)
 {
        struct fib_entry_notifier_info *fen_info;
        struct fib_nh_notifier_info *fnh_info;
 
-       switch (fib_work->event) {
+       switch (fib_event->event) {
        case FIB_EVENT_ENTRY_REPLACE:
        case FIB_EVENT_ENTRY_DEL:
                fen_info = container_of(info, struct fib_entry_notifier_info,
                                        info);
-               fib_work->fen_info = *fen_info;
+               fib_event->fen_info = *fen_info;
                /* Take reference on fib_info to prevent it from being
-                * freed while work is queued. Release it afterwards.
+                * freed while event is queued. Release it afterwards.
                 */
-               fib_info_hold(fib_work->fen_info.fi);
+               fib_info_hold(fib_event->fen_info.fi);
                break;
        case FIB_EVENT_NH_ADD:
        case FIB_EVENT_NH_DEL:
                fnh_info = container_of(info, struct fib_nh_notifier_info,
                                        info);
-               fib_work->fnh_info = *fnh_info;
-               fib_info_hold(fib_work->fnh_info.fib_nh->nh_parent);
+               fib_event->fnh_info = *fnh_info;
+               fib_info_hold(fib_event->fnh_info.fib_nh->nh_parent);
                break;
        }
 }
 
-static int mlxsw_sp_router_fib6_event(struct mlxsw_sp_fib_event_work *fib_work,
+static int mlxsw_sp_router_fib6_event(struct mlxsw_sp_fib_event *fib_event,
                                      struct fib_notifier_info *info)
 {
        struct fib6_entry_notifier_info *fen6_info;
        int err;
 
-       switch (fib_work->event) {
+       switch (fib_event->event) {
        case FIB_EVENT_ENTRY_REPLACE:
        case FIB_EVENT_ENTRY_APPEND:
        case FIB_EVENT_ENTRY_DEL:
                fen6_info = container_of(info, struct fib6_entry_notifier_info,
                                         info);
-               err = mlxsw_sp_router_fib6_work_init(&fib_work->fib6_work,
-                                                    fen6_info);
+               err = mlxsw_sp_router_fib6_event_init(&fib_event->fib6_event,
+                                                     fen6_info);
                if (err)
                        return err;
                break;
@@ -6132,20 +6477,20 @@ static int mlxsw_sp_router_fib6_event(struct mlxsw_sp_fib_event_work *fib_work,
 }
 
 static void
-mlxsw_sp_router_fibmr_event(struct mlxsw_sp_fib_event_work *fib_work,
+mlxsw_sp_router_fibmr_event(struct mlxsw_sp_fib_event *fib_event,
                            struct fib_notifier_info *info)
 {
-       switch (fib_work->event) {
+       switch (fib_event->event) {
        case FIB_EVENT_ENTRY_REPLACE:
        case FIB_EVENT_ENTRY_ADD:
        case FIB_EVENT_ENTRY_DEL:
-               memcpy(&fib_work->men_info, info, sizeof(fib_work->men_info));
-               mr_cache_hold(fib_work->men_info.mfc);
+               memcpy(&fib_event->men_info, info, sizeof(fib_event->men_info));
+               mr_cache_hold(fib_event->men_info.mfc);
                break;
        case FIB_EVENT_VIF_ADD:
        case FIB_EVENT_VIF_DEL:
-               memcpy(&fib_work->ven_info, info, sizeof(fib_work->ven_info));
-               dev_hold(fib_work->ven_info.dev);
+               memcpy(&fib_event->ven_info, info, sizeof(fib_event->ven_info));
+               dev_hold(fib_event->ven_info.dev);
                break;
        }
 }
@@ -6202,7 +6547,7 @@ static int mlxsw_sp_router_fib_rule_event(unsigned long event,
 static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
                                     unsigned long event, void *ptr)
 {
-       struct mlxsw_sp_fib_event_work *fib_work;
+       struct mlxsw_sp_fib_event *fib_event;
        struct fib_notifier_info *info = ptr;
        struct mlxsw_sp_router *router;
        int err;
@@ -6252,37 +6597,39 @@ static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
                break;
        }
 
-       fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC);
-       if (!fib_work)
+       fib_event = kzalloc(sizeof(*fib_event), GFP_ATOMIC);
+       if (!fib_event)
                return NOTIFY_BAD;
 
-       fib_work->mlxsw_sp = router->mlxsw_sp;
-       fib_work->event = event;
+       fib_event->mlxsw_sp = router->mlxsw_sp;
+       fib_event->event = event;
+       fib_event->family = info->family;
 
        switch (info->family) {
        case AF_INET:
-               INIT_WORK(&fib_work->work, mlxsw_sp_router_fib4_event_work);
-               mlxsw_sp_router_fib4_event(fib_work, info);
+               mlxsw_sp_router_fib4_event(fib_event, info);
                break;
        case AF_INET6:
-               INIT_WORK(&fib_work->work, mlxsw_sp_router_fib6_event_work);
-               err = mlxsw_sp_router_fib6_event(fib_work, info);
+               err = mlxsw_sp_router_fib6_event(fib_event, info);
                if (err)
                        goto err_fib_event;
                break;
        case RTNL_FAMILY_IP6MR:
        case RTNL_FAMILY_IPMR:
-               INIT_WORK(&fib_work->work, mlxsw_sp_router_fibmr_event_work);
-               mlxsw_sp_router_fibmr_event(fib_work, info);
+               mlxsw_sp_router_fibmr_event(fib_event, info);
                break;
        }
 
-       mlxsw_core_schedule_work(&fib_work->work);
+       /* Enqueue the event and trigger the work */
+       spin_lock_bh(&router->fib_event_queue_lock);
+       list_add_tail(&fib_event->list, &router->fib_event_queue);
+       spin_unlock_bh(&router->fib_event_queue_lock);
+       mlxsw_core_schedule_work(&router->fib_event_work);
 
        return NOTIFY_DONE;
 
 err_fib_event:
-       kfree(fib_work);
+       kfree(fib_event);
        return NOTIFY_BAD;
 }
 
@@ -8057,6 +8404,45 @@ static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
        mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
 }
 
+static const struct mlxsw_sp_router_ll_ops mlxsw_sp_router_ll_basic_ops = {
+       .ralta_write = mlxsw_sp_router_ll_basic_ralta_write,
+       .ralst_write = mlxsw_sp_router_ll_basic_ralst_write,
+       .raltb_write = mlxsw_sp_router_ll_basic_raltb_write,
+       .fib_entry_op_ctx_size = sizeof(struct mlxsw_sp_fib_entry_op_ctx_basic),
+       .fib_entry_pack = mlxsw_sp_router_ll_basic_fib_entry_pack,
+       .fib_entry_act_remote_pack = mlxsw_sp_router_ll_basic_fib_entry_act_remote_pack,
+       .fib_entry_act_local_pack = mlxsw_sp_router_ll_basic_fib_entry_act_local_pack,
+       .fib_entry_act_ip2me_pack = mlxsw_sp_router_ll_basic_fib_entry_act_ip2me_pack,
+       .fib_entry_act_ip2me_tun_pack = mlxsw_sp_router_ll_basic_fib_entry_act_ip2me_tun_pack,
+       .fib_entry_commit = mlxsw_sp_router_ll_basic_fib_entry_commit,
+       .fib_entry_is_committed = mlxsw_sp_router_ll_basic_fib_entry_is_committed,
+};
+
+static int mlxsw_sp_router_ll_op_ctx_init(struct mlxsw_sp_router *router)
+{
+       size_t max_size = 0;
+       int i;
+
+       for (i = 0; i < MLXSW_SP_L3_PROTO_MAX; i++) {
+               size_t size = router->proto_ll_ops[i]->fib_entry_op_ctx_size;
+
+               if (size > max_size)
+                       max_size = size;
+       }
+       router->ll_op_ctx = kzalloc(sizeof(*router->ll_op_ctx) + max_size,
+                                   GFP_KERNEL);
+       if (!router->ll_op_ctx)
+               return -ENOMEM;
+       INIT_LIST_HEAD(&router->ll_op_ctx->fib_entry_priv_list);
+       return 0;
+}
+
+static void mlxsw_sp_router_ll_op_ctx_fini(struct mlxsw_sp_router *router)
+{
+       WARN_ON(!list_empty(&router->ll_op_ctx->fib_entry_priv_list));
+       kfree(router->ll_op_ctx);
+}
+
 int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp,
                         struct netlink_ext_ack *extack)
 {
@@ -8070,6 +8456,13 @@ int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp,
        mlxsw_sp->router = router;
        router->mlxsw_sp = mlxsw_sp;
 
+       router->proto_ll_ops[MLXSW_SP_L3_PROTO_IPV4] = &mlxsw_sp_router_ll_basic_ops;
+       router->proto_ll_ops[MLXSW_SP_L3_PROTO_IPV6] = &mlxsw_sp_router_ll_basic_ops;
+
+       err = mlxsw_sp_router_ll_op_ctx_init(router);
+       if (err)
+               goto err_ll_op_ctx_init;
+
        INIT_LIST_HEAD(&mlxsw_sp->router->nexthop_neighs_list);
        err = __mlxsw_sp_router_init(mlxsw_sp);
        if (err)
@@ -8118,6 +8511,10 @@ int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp,
        if (err)
                goto err_dscp_init;
 
+       INIT_WORK(&router->fib_event_work, mlxsw_sp_router_fib_event_work);
+       INIT_LIST_HEAD(&router->fib_event_queue);
+       spin_lock_init(&router->fib_event_queue_lock);
+
        router->inetaddr_nb.notifier_call = mlxsw_sp_inetaddr_event;
        err = register_inetaddr_notifier(&router->inetaddr_nb);
        if (err)
@@ -8151,6 +8548,7 @@ err_register_inet6addr_notifier:
        unregister_inetaddr_notifier(&router->inetaddr_nb);
 err_register_inetaddr_notifier:
        mlxsw_core_flush_owq();
+       WARN_ON(!list_empty(&router->fib_event_queue));
 err_dscp_init:
 err_mp_hash_init:
        mlxsw_sp_neigh_fini(mlxsw_sp);
@@ -8171,6 +8569,8 @@ err_ipips_init:
 err_rifs_init:
        __mlxsw_sp_router_fini(mlxsw_sp);
 err_router_init:
+       mlxsw_sp_router_ll_op_ctx_fini(router);
+err_ll_op_ctx_init:
        mutex_destroy(&mlxsw_sp->router->lock);
        kfree(mlxsw_sp->router);
        return err;
@@ -8184,6 +8584,7 @@ void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
        unregister_inet6addr_notifier(&mlxsw_sp->router->inet6addr_nb);
        unregister_inetaddr_notifier(&mlxsw_sp->router->inetaddr_nb);
        mlxsw_core_flush_owq();
+       WARN_ON(!list_empty(&mlxsw_sp->router->fib_event_queue));
        mlxsw_sp_neigh_fini(mlxsw_sp);
        mlxsw_sp_vrs_fini(mlxsw_sp);
        mlxsw_sp_mr_fini(mlxsw_sp);
@@ -8193,6 +8594,7 @@ void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
        mlxsw_sp_ipips_fini(mlxsw_sp);
        mlxsw_sp_rifs_fini(mlxsw_sp);
        __mlxsw_sp_router_fini(mlxsw_sp);
+       mlxsw_sp_router_ll_op_ctx_fini(mlxsw_sp->router);
        mutex_destroy(&mlxsw_sp->router->lock);
        kfree(mlxsw_sp->router);
 }