|
@@ -43,6 +43,7 @@
|
|
|
#include <net/netevent.h>
|
|
|
#include <net/neighbour.h>
|
|
|
#include <net/arp.h>
|
|
|
+#include <net/ip_fib.h>
|
|
|
|
|
|
#include "spectrum.h"
|
|
|
#include "core.h"
|
|
@@ -122,17 +123,20 @@ struct mlxsw_sp_nexthop_group;
|
|
|
|
|
|
struct mlxsw_sp_fib_entry {
|
|
|
struct rhash_head ht_node;
|
|
|
+ struct list_head list;
|
|
|
struct mlxsw_sp_fib_key key;
|
|
|
enum mlxsw_sp_fib_entry_type type;
|
|
|
unsigned int ref_count;
|
|
|
u16 rif; /* used for action local */
|
|
|
struct mlxsw_sp_vr *vr;
|
|
|
+ struct fib_info *fi;
|
|
|
struct list_head nexthop_group_node;
|
|
|
struct mlxsw_sp_nexthop_group *nh_group;
|
|
|
};
|
|
|
|
|
|
struct mlxsw_sp_fib {
|
|
|
struct rhashtable ht;
|
|
|
+ struct list_head entry_list;
|
|
|
unsigned long prefix_ref_count[MLXSW_SP_PREFIX_COUNT];
|
|
|
struct mlxsw_sp_prefix_usage prefix_usage;
|
|
|
};
|
|
@@ -154,6 +158,7 @@ static int mlxsw_sp_fib_entry_insert(struct mlxsw_sp_fib *fib,
|
|
|
mlxsw_sp_fib_ht_params);
|
|
|
if (err)
|
|
|
return err;
|
|
|
+ list_add_tail(&fib_entry->list, &fib->entry_list);
|
|
|
if (fib->prefix_ref_count[prefix_len]++ == 0)
|
|
|
mlxsw_sp_prefix_usage_set(&fib->prefix_usage, prefix_len);
|
|
|
return 0;
|
|
@@ -166,6 +171,7 @@ static void mlxsw_sp_fib_entry_remove(struct mlxsw_sp_fib *fib,
|
|
|
|
|
|
if (--fib->prefix_ref_count[prefix_len] == 0)
|
|
|
mlxsw_sp_prefix_usage_clear(&fib->prefix_usage, prefix_len);
|
|
|
+ list_del(&fib_entry->list);
|
|
|
rhashtable_remove_fast(&fib->ht, &fib_entry->ht_node,
|
|
|
mlxsw_sp_fib_ht_params);
|
|
|
}
|
|
@@ -216,6 +222,7 @@ static struct mlxsw_sp_fib *mlxsw_sp_fib_create(void)
|
|
|
err = rhashtable_init(&fib->ht, &mlxsw_sp_fib_ht_params);
|
|
|
if (err)
|
|
|
goto err_rhashtable_init;
|
|
|
+ INIT_LIST_HEAD(&fib->entry_list);
|
|
|
return fib;
|
|
|
|
|
|
err_rhashtable_init:
|
|
@@ -1520,85 +1527,6 @@ static void mlxsw_sp_nexthop_group_put(struct mlxsw_sp *mlxsw_sp,
|
|
|
mlxsw_sp_nexthop_group_destroy(mlxsw_sp, nh_grp);
|
|
|
}
|
|
|
|
|
|
-static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
|
|
|
-{
|
|
|
- struct mlxsw_resources *resources;
|
|
|
- char rgcr_pl[MLXSW_REG_RGCR_LEN];
|
|
|
- int err;
|
|
|
-
|
|
|
- resources = mlxsw_core_resources_get(mlxsw_sp->core);
|
|
|
- if (!resources->max_rif_valid)
|
|
|
- return -EIO;
|
|
|
-
|
|
|
- mlxsw_sp->rifs = kcalloc(resources->max_rif,
|
|
|
- sizeof(struct mlxsw_sp_rif *), GFP_KERNEL);
|
|
|
- if (!mlxsw_sp->rifs)
|
|
|
- return -ENOMEM;
|
|
|
-
|
|
|
- mlxsw_reg_rgcr_pack(rgcr_pl, true);
|
|
|
- mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, resources->max_rif);
|
|
|
- err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
|
|
|
- if (err)
|
|
|
- goto err_rgcr_fail;
|
|
|
-
|
|
|
- return 0;
|
|
|
-
|
|
|
-err_rgcr_fail:
|
|
|
- kfree(mlxsw_sp->rifs);
|
|
|
- return err;
|
|
|
-}
|
|
|
-
|
|
|
-static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
|
|
|
-{
|
|
|
- struct mlxsw_resources *resources;
|
|
|
- char rgcr_pl[MLXSW_REG_RGCR_LEN];
|
|
|
- int i;
|
|
|
-
|
|
|
- mlxsw_reg_rgcr_pack(rgcr_pl, false);
|
|
|
- mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
|
|
|
-
|
|
|
- resources = mlxsw_core_resources_get(mlxsw_sp->core);
|
|
|
- for (i = 0; i < resources->max_rif; i++)
|
|
|
- WARN_ON_ONCE(mlxsw_sp->rifs[i]);
|
|
|
-
|
|
|
- kfree(mlxsw_sp->rifs);
|
|
|
-}
|
|
|
-
|
|
|
-int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
|
|
|
-{
|
|
|
- int err;
|
|
|
-
|
|
|
- INIT_LIST_HEAD(&mlxsw_sp->router.nexthop_neighs_list);
|
|
|
- INIT_LIST_HEAD(&mlxsw_sp->router.nexthop_group_list);
|
|
|
- err = __mlxsw_sp_router_init(mlxsw_sp);
|
|
|
- if (err)
|
|
|
- return err;
|
|
|
-
|
|
|
- mlxsw_sp_lpm_init(mlxsw_sp);
|
|
|
- err = mlxsw_sp_vrs_init(mlxsw_sp);
|
|
|
- if (err)
|
|
|
- goto err_vrs_init;
|
|
|
-
|
|
|
- err = mlxsw_sp_neigh_init(mlxsw_sp);
|
|
|
- if (err)
|
|
|
- goto err_neigh_init;
|
|
|
-
|
|
|
- return 0;
|
|
|
-
|
|
|
-err_neigh_init:
|
|
|
- mlxsw_sp_vrs_fini(mlxsw_sp);
|
|
|
-err_vrs_init:
|
|
|
- __mlxsw_sp_router_fini(mlxsw_sp);
|
|
|
- return err;
|
|
|
-}
|
|
|
-
|
|
|
-void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
|
|
|
-{
|
|
|
- mlxsw_sp_neigh_fini(mlxsw_sp);
|
|
|
- mlxsw_sp_vrs_fini(mlxsw_sp);
|
|
|
- __mlxsw_sp_router_fini(mlxsw_sp);
|
|
|
-}
|
|
|
-
|
|
|
static int mlxsw_sp_fib_entry_op4_remote(struct mlxsw_sp *mlxsw_sp,
|
|
|
struct mlxsw_sp_fib_entry *fib_entry,
|
|
|
enum mlxsw_reg_ralue_op op)
|
|
@@ -1706,94 +1634,98 @@ static int mlxsw_sp_fib_entry_del(struct mlxsw_sp *mlxsw_sp,
|
|
|
MLXSW_REG_RALUE_OP_WRITE_DELETE);
|
|
|
}
|
|
|
|
|
|
-struct mlxsw_sp_router_fib4_add_info {
|
|
|
- struct switchdev_trans_item tritem;
|
|
|
- struct mlxsw_sp *mlxsw_sp;
|
|
|
- struct mlxsw_sp_fib_entry *fib_entry;
|
|
|
-};
|
|
|
-
|
|
|
-static void mlxsw_sp_router_fib4_add_info_destroy(void const *data)
|
|
|
-{
|
|
|
- const struct mlxsw_sp_router_fib4_add_info *info = data;
|
|
|
- struct mlxsw_sp_fib_entry *fib_entry = info->fib_entry;
|
|
|
- struct mlxsw_sp *mlxsw_sp = info->mlxsw_sp;
|
|
|
- struct mlxsw_sp_vr *vr = fib_entry->vr;
|
|
|
-
|
|
|
- mlxsw_sp_fib_entry_destroy(fib_entry);
|
|
|
- mlxsw_sp_vr_put(mlxsw_sp, vr);
|
|
|
- kfree(info);
|
|
|
-}
|
|
|
-
|
|
|
static int
|
|
|
mlxsw_sp_router_fib4_entry_init(struct mlxsw_sp *mlxsw_sp,
|
|
|
- const struct switchdev_obj_ipv4_fib *fib4,
|
|
|
+ const struct fib_entry_notifier_info *fen_info,
|
|
|
struct mlxsw_sp_fib_entry *fib_entry)
|
|
|
{
|
|
|
- struct fib_info *fi = fib4->fi;
|
|
|
+ struct fib_info *fi = fen_info->fi;
|
|
|
+ struct mlxsw_sp_rif *r;
|
|
|
+ int nhsel;
|
|
|
+ int err;
|
|
|
|
|
|
- if (fib4->type == RTN_LOCAL || fib4->type == RTN_BROADCAST) {
|
|
|
+ if (fen_info->type == RTN_LOCAL || fen_info->type == RTN_BROADCAST) {
|
|
|
fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
|
|
|
return 0;
|
|
|
}
|
|
|
- if (fib4->type != RTN_UNICAST)
|
|
|
+ if (fen_info->type != RTN_UNICAST)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- if (fi->fib_scope != RT_SCOPE_UNIVERSE) {
|
|
|
- struct mlxsw_sp_rif *r;
|
|
|
+ for (nhsel = 0; nhsel < fi->fib_nhs; nhsel++) {
|
|
|
+ const struct fib_nh *nh = &fi->fib_nh[nhsel];
|
|
|
+
|
|
|
+ if (!nh->nh_dev)
|
|
|
+ continue;
|
|
|
+ r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, nh->nh_dev);
|
|
|
+ if (!r) {
|
|
|
+ /* In case router interface is not found for
|
|
|
+ * at least one of the nexthops, that means
|
|
|
+ * the nexthop points to some device unrelated
|
|
|
+ * to us. Set trap and pass the packets for
|
|
|
+ * this prefix to kernel.
|
|
|
+ */
|
|
|
+ fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
+ if (fi->fib_scope != RT_SCOPE_UNIVERSE) {
|
|
|
fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
|
|
|
- r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, fi->fib_dev);
|
|
|
- if (!r)
|
|
|
- return -EINVAL;
|
|
|
fib_entry->rif = r->rif;
|
|
|
- return 0;
|
|
|
+ } else {
|
|
|
+ fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
|
|
|
+ err = mlxsw_sp_nexthop_group_get(mlxsw_sp, fib_entry, fi);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
}
|
|
|
- fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE;
|
|
|
- return mlxsw_sp_nexthop_group_get(mlxsw_sp, fib_entry, fi);
|
|
|
+ fib_info_offload_inc(fen_info->fi);
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
static void
|
|
|
mlxsw_sp_router_fib4_entry_fini(struct mlxsw_sp *mlxsw_sp,
|
|
|
struct mlxsw_sp_fib_entry *fib_entry)
|
|
|
{
|
|
|
- if (fib_entry->type != MLXSW_SP_FIB_ENTRY_TYPE_REMOTE)
|
|
|
- return;
|
|
|
- mlxsw_sp_nexthop_group_put(mlxsw_sp, fib_entry);
|
|
|
+ if (fib_entry->type != MLXSW_SP_FIB_ENTRY_TYPE_TRAP)
|
|
|
+ fib_info_offload_dec(fib_entry->fi);
|
|
|
+ if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_REMOTE)
|
|
|
+ mlxsw_sp_nexthop_group_put(mlxsw_sp, fib_entry);
|
|
|
}
|
|
|
|
|
|
static struct mlxsw_sp_fib_entry *
|
|
|
mlxsw_sp_fib_entry_get(struct mlxsw_sp *mlxsw_sp,
|
|
|
- const struct switchdev_obj_ipv4_fib *fib4)
|
|
|
+ const struct fib_entry_notifier_info *fen_info)
|
|
|
{
|
|
|
struct mlxsw_sp_fib_entry *fib_entry;
|
|
|
- struct fib_info *fi = fib4->fi;
|
|
|
+ struct fib_info *fi = fen_info->fi;
|
|
|
struct mlxsw_sp_vr *vr;
|
|
|
int err;
|
|
|
|
|
|
- vr = mlxsw_sp_vr_get(mlxsw_sp, fib4->dst_len, fib4->tb_id,
|
|
|
+ vr = mlxsw_sp_vr_get(mlxsw_sp, fen_info->dst_len, fen_info->tb_id,
|
|
|
MLXSW_SP_L3_PROTO_IPV4);
|
|
|
if (IS_ERR(vr))
|
|
|
return ERR_CAST(vr);
|
|
|
|
|
|
- fib_entry = mlxsw_sp_fib_entry_lookup(vr->fib, &fib4->dst,
|
|
|
- sizeof(fib4->dst),
|
|
|
- fib4->dst_len, fi->fib_dev);
|
|
|
+ fib_entry = mlxsw_sp_fib_entry_lookup(vr->fib, &fen_info->dst,
|
|
|
+ sizeof(fen_info->dst),
|
|
|
+ fen_info->dst_len, fi->fib_dev);
|
|
|
if (fib_entry) {
|
|
|
/* Already exists, just take a reference */
|
|
|
fib_entry->ref_count++;
|
|
|
return fib_entry;
|
|
|
}
|
|
|
- fib_entry = mlxsw_sp_fib_entry_create(vr->fib, &fib4->dst,
|
|
|
- sizeof(fib4->dst),
|
|
|
- fib4->dst_len, fi->fib_dev);
|
|
|
+ fib_entry = mlxsw_sp_fib_entry_create(vr->fib, &fen_info->dst,
|
|
|
+ sizeof(fen_info->dst),
|
|
|
+ fen_info->dst_len, fi->fib_dev);
|
|
|
if (!fib_entry) {
|
|
|
err = -ENOMEM;
|
|
|
goto err_fib_entry_create;
|
|
|
}
|
|
|
fib_entry->vr = vr;
|
|
|
+ fib_entry->fi = fi;
|
|
|
fib_entry->ref_count = 1;
|
|
|
|
|
|
- err = mlxsw_sp_router_fib4_entry_init(mlxsw_sp, fib4, fib_entry);
|
|
|
+ err = mlxsw_sp_router_fib4_entry_init(mlxsw_sp, fen_info, fib_entry);
|
|
|
if (err)
|
|
|
goto err_fib4_entry_init;
|
|
|
|
|
@@ -1809,17 +1741,19 @@ err_fib_entry_create:
|
|
|
|
|
|
static struct mlxsw_sp_fib_entry *
|
|
|
mlxsw_sp_fib_entry_find(struct mlxsw_sp *mlxsw_sp,
|
|
|
- const struct switchdev_obj_ipv4_fib *fib4)
|
|
|
+ const struct fib_entry_notifier_info *fen_info)
|
|
|
{
|
|
|
struct mlxsw_sp_vr *vr;
|
|
|
|
|
|
- vr = mlxsw_sp_vr_find(mlxsw_sp, fib4->tb_id, MLXSW_SP_L3_PROTO_IPV4);
|
|
|
+ vr = mlxsw_sp_vr_find(mlxsw_sp, fen_info->tb_id,
|
|
|
+ MLXSW_SP_L3_PROTO_IPV4);
|
|
|
if (!vr)
|
|
|
return NULL;
|
|
|
|
|
|
- return mlxsw_sp_fib_entry_lookup(vr->fib, &fib4->dst,
|
|
|
- sizeof(fib4->dst), fib4->dst_len,
|
|
|
- fib4->fi->fib_dev);
|
|
|
+ return mlxsw_sp_fib_entry_lookup(vr->fib, &fen_info->dst,
|
|
|
+ sizeof(fen_info->dst),
|
|
|
+ fen_info->dst_len,
|
|
|
+ fen_info->fi->fib_dev);
|
|
|
}
|
|
|
|
|
|
static void mlxsw_sp_fib_entry_put(struct mlxsw_sp *mlxsw_sp,
|
|
@@ -1834,60 +1768,43 @@ static void mlxsw_sp_fib_entry_put(struct mlxsw_sp *mlxsw_sp,
|
|
|
mlxsw_sp_vr_put(mlxsw_sp, vr);
|
|
|
}
|
|
|
|
|
|
-static int
|
|
|
-mlxsw_sp_router_fib4_add_prepare(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
|
- const struct switchdev_obj_ipv4_fib *fib4,
|
|
|
- struct switchdev_trans *trans)
|
|
|
+static void mlxsw_sp_fib_entry_put_all(struct mlxsw_sp *mlxsw_sp,
|
|
|
+ struct mlxsw_sp_fib_entry *fib_entry)
|
|
|
{
|
|
|
- struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
|
|
|
- struct mlxsw_sp_router_fib4_add_info *info;
|
|
|
- struct mlxsw_sp_fib_entry *fib_entry;
|
|
|
- int err;
|
|
|
-
|
|
|
- fib_entry = mlxsw_sp_fib_entry_get(mlxsw_sp, fib4);
|
|
|
- if (IS_ERR(fib_entry))
|
|
|
- return PTR_ERR(fib_entry);
|
|
|
+ unsigned int last_ref_count;
|
|
|
|
|
|
- info = kmalloc(sizeof(*info), GFP_KERNEL);
|
|
|
- if (!info) {
|
|
|
- err = -ENOMEM;
|
|
|
- goto err_alloc_info;
|
|
|
- }
|
|
|
- info->mlxsw_sp = mlxsw_sp;
|
|
|
- info->fib_entry = fib_entry;
|
|
|
- switchdev_trans_item_enqueue(trans, info,
|
|
|
- mlxsw_sp_router_fib4_add_info_destroy,
|
|
|
- &info->tritem);
|
|
|
- return 0;
|
|
|
-
|
|
|
-err_alloc_info:
|
|
|
- mlxsw_sp_fib_entry_put(mlxsw_sp, fib_entry);
|
|
|
- return err;
|
|
|
+ do {
|
|
|
+ last_ref_count = fib_entry->ref_count;
|
|
|
+ mlxsw_sp_fib_entry_put(mlxsw_sp, fib_entry);
|
|
|
+ } while (last_ref_count != 1);
|
|
|
}
|
|
|
|
|
|
-static int
|
|
|
-mlxsw_sp_router_fib4_add_commit(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
|
- const struct switchdev_obj_ipv4_fib *fib4,
|
|
|
- struct switchdev_trans *trans)
|
|
|
+static int mlxsw_sp_router_fib4_add(struct mlxsw_sp *mlxsw_sp,
|
|
|
+ struct fib_entry_notifier_info *fen_info)
|
|
|
{
|
|
|
- struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
|
|
|
- struct mlxsw_sp_router_fib4_add_info *info;
|
|
|
struct mlxsw_sp_fib_entry *fib_entry;
|
|
|
struct mlxsw_sp_vr *vr;
|
|
|
int err;
|
|
|
|
|
|
- info = switchdev_trans_item_dequeue(trans);
|
|
|
- fib_entry = info->fib_entry;
|
|
|
- kfree(info);
|
|
|
+ if (mlxsw_sp->router.aborted)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ fib_entry = mlxsw_sp_fib_entry_get(mlxsw_sp, fen_info);
|
|
|
+ if (IS_ERR(fib_entry)) {
|
|
|
+ dev_warn(mlxsw_sp->bus_info->dev, "Failed to get FIB4 entry being added.\n");
|
|
|
+ return PTR_ERR(fib_entry);
|
|
|
+ }
|
|
|
|
|
|
if (fib_entry->ref_count != 1)
|
|
|
return 0;
|
|
|
|
|
|
vr = fib_entry->vr;
|
|
|
err = mlxsw_sp_fib_entry_insert(vr->fib, fib_entry);
|
|
|
- if (err)
|
|
|
+ if (err) {
|
|
|
+ dev_warn(mlxsw_sp->bus_info->dev, "Failed to insert FIB4 entry being added.\n");
|
|
|
goto err_fib_entry_insert;
|
|
|
- err = mlxsw_sp_fib_entry_update(mlxsw_sp_port->mlxsw_sp, fib_entry);
|
|
|
+ }
|
|
|
+ err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
|
|
|
if (err)
|
|
|
goto err_fib_entry_add;
|
|
|
return 0;
|
|
@@ -1899,24 +1816,15 @@ err_fib_entry_insert:
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
-int mlxsw_sp_router_fib4_add(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
|
- const struct switchdev_obj_ipv4_fib *fib4,
|
|
|
- struct switchdev_trans *trans)
|
|
|
-{
|
|
|
- if (switchdev_trans_ph_prepare(trans))
|
|
|
- return mlxsw_sp_router_fib4_add_prepare(mlxsw_sp_port,
|
|
|
- fib4, trans);
|
|
|
- return mlxsw_sp_router_fib4_add_commit(mlxsw_sp_port,
|
|
|
- fib4, trans);
|
|
|
-}
|
|
|
-
|
|
|
-int mlxsw_sp_router_fib4_del(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
|
- const struct switchdev_obj_ipv4_fib *fib4)
|
|
|
+static int mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp,
|
|
|
+ struct fib_entry_notifier_info *fen_info)
|
|
|
{
|
|
|
- struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
|
|
|
struct mlxsw_sp_fib_entry *fib_entry;
|
|
|
|
|
|
- fib_entry = mlxsw_sp_fib_entry_find(mlxsw_sp, fib4);
|
|
|
+ if (mlxsw_sp->router.aborted)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ fib_entry = mlxsw_sp_fib_entry_find(mlxsw_sp, fen_info);
|
|
|
if (!fib_entry) {
|
|
|
dev_warn(mlxsw_sp->bus_info->dev, "Failed to find FIB4 entry being removed.\n");
|
|
|
return -ENOENT;
|
|
@@ -1930,3 +1838,172 @@ int mlxsw_sp_router_fib4_del(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
|
mlxsw_sp_fib_entry_put(mlxsw_sp, fib_entry);
|
|
|
return 0;
|
|
|
}
|
|
|
+
|
|
|
+static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp)
|
|
|
+{
|
|
|
+ char ralta_pl[MLXSW_REG_RALTA_LEN];
|
|
|
+ char ralst_pl[MLXSW_REG_RALST_LEN];
|
|
|
+ char raltb_pl[MLXSW_REG_RALTB_LEN];
|
|
|
+ char ralue_pl[MLXSW_REG_RALUE_LEN];
|
|
|
+ int err;
|
|
|
+
|
|
|
+ mlxsw_reg_ralta_pack(ralta_pl, true, MLXSW_REG_RALXX_PROTOCOL_IPV4,
|
|
|
+ MLXSW_SP_LPM_TREE_MIN);
|
|
|
+ err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ mlxsw_reg_ralst_pack(ralst_pl, 0xff, MLXSW_SP_LPM_TREE_MIN);
|
|
|
+ err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ mlxsw_reg_raltb_pack(raltb_pl, 0, MLXSW_REG_RALXX_PROTOCOL_IPV4, 0);
|
|
|
+ err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ mlxsw_reg_ralue_pack4(ralue_pl, MLXSW_SP_L3_PROTO_IPV4,
|
|
|
+ MLXSW_REG_RALUE_OP_WRITE_WRITE, 0, 0, 0);
|
|
|
+ mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
|
|
|
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
|
|
|
+}
|
|
|
+
|
|
|
+static void mlxsw_sp_router_fib4_abort(struct mlxsw_sp *mlxsw_sp)
|
|
|
+{
|
|
|
+ struct mlxsw_resources *resources;
|
|
|
+ struct mlxsw_sp_fib_entry *fib_entry;
|
|
|
+ struct mlxsw_sp_fib_entry *tmp;
|
|
|
+ struct mlxsw_sp_vr *vr;
|
|
|
+ int i;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ resources = mlxsw_core_resources_get(mlxsw_sp->core);
|
|
|
+ for (i = 0; i < resources->max_virtual_routers; i++) {
|
|
|
+ vr = &mlxsw_sp->router.vrs[i];
|
|
|
+ if (!vr->used)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ list_for_each_entry_safe(fib_entry, tmp,
|
|
|
+ &vr->fib->entry_list, list) {
|
|
|
+ bool do_break = &tmp->list == &vr->fib->entry_list;
|
|
|
+
|
|
|
+ mlxsw_sp_fib_entry_del(mlxsw_sp, fib_entry);
|
|
|
+ mlxsw_sp_fib_entry_remove(fib_entry->vr->fib,
|
|
|
+ fib_entry);
|
|
|
+ mlxsw_sp_fib_entry_put_all(mlxsw_sp, fib_entry);
|
|
|
+ if (do_break)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ mlxsw_sp->router.aborted = true;
|
|
|
+ err = mlxsw_sp_router_set_abort_trap(mlxsw_sp);
|
|
|
+ if (err)
|
|
|
+ dev_warn(mlxsw_sp->bus_info->dev, "Failed to set abort trap.\n");
|
|
|
+}
|
|
|
+
|
|
|
+static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
|
|
|
+{
|
|
|
+ struct mlxsw_resources *resources;
|
|
|
+ char rgcr_pl[MLXSW_REG_RGCR_LEN];
|
|
|
+ int err;
|
|
|
+
|
|
|
+ resources = mlxsw_core_resources_get(mlxsw_sp->core);
|
|
|
+ if (!resources->max_rif_valid)
|
|
|
+ return -EIO;
|
|
|
+
|
|
|
+ mlxsw_sp->rifs = kcalloc(resources->max_rif,
|
|
|
+ sizeof(struct mlxsw_sp_rif *), GFP_KERNEL);
|
|
|
+ if (!mlxsw_sp->rifs)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ mlxsw_reg_rgcr_pack(rgcr_pl, true);
|
|
|
+ mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, resources->max_rif);
|
|
|
+ err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
|
|
|
+ if (err)
|
|
|
+ goto err_rgcr_fail;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+err_rgcr_fail:
|
|
|
+ kfree(mlxsw_sp->rifs);
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
|
|
|
+{
|
|
|
+ struct mlxsw_resources *resources;
|
|
|
+ char rgcr_pl[MLXSW_REG_RGCR_LEN];
|
|
|
+ int i;
|
|
|
+
|
|
|
+ mlxsw_reg_rgcr_pack(rgcr_pl, false);
|
|
|
+ mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
|
|
|
+
|
|
|
+ resources = mlxsw_core_resources_get(mlxsw_sp->core);
|
|
|
+ for (i = 0; i < resources->max_rif; i++)
|
|
|
+ WARN_ON_ONCE(mlxsw_sp->rifs[i]);
|
|
|
+
|
|
|
+ kfree(mlxsw_sp->rifs);
|
|
|
+}
|
|
|
+
|
|
|
+static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
|
|
|
+ unsigned long event, void *ptr)
|
|
|
+{
|
|
|
+ struct mlxsw_sp *mlxsw_sp = container_of(nb, struct mlxsw_sp, fib_nb);
|
|
|
+ struct fib_entry_notifier_info *fen_info = ptr;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ switch (event) {
|
|
|
+ case FIB_EVENT_ENTRY_ADD:
|
|
|
+ err = mlxsw_sp_router_fib4_add(mlxsw_sp, fen_info);
|
|
|
+ if (err)
|
|
|
+ mlxsw_sp_router_fib4_abort(mlxsw_sp);
|
|
|
+ break;
|
|
|
+ case FIB_EVENT_ENTRY_DEL:
|
|
|
+ mlxsw_sp_router_fib4_del(mlxsw_sp, fen_info);
|
|
|
+ break;
|
|
|
+ case FIB_EVENT_RULE_ADD: /* fall through */
|
|
|
+ case FIB_EVENT_RULE_DEL:
|
|
|
+ mlxsw_sp_router_fib4_abort(mlxsw_sp);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return NOTIFY_DONE;
|
|
|
+}
|
|
|
+
|
|
|
+int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
|
|
|
+{
|
|
|
+ int err;
|
|
|
+
|
|
|
+ INIT_LIST_HEAD(&mlxsw_sp->router.nexthop_neighs_list);
|
|
|
+ INIT_LIST_HEAD(&mlxsw_sp->router.nexthop_group_list);
|
|
|
+ err = __mlxsw_sp_router_init(mlxsw_sp);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ mlxsw_sp_lpm_init(mlxsw_sp);
|
|
|
+ err = mlxsw_sp_vrs_init(mlxsw_sp);
|
|
|
+ if (err)
|
|
|
+ goto err_vrs_init;
|
|
|
+
|
|
|
+ err = mlxsw_sp_neigh_init(mlxsw_sp);
|
|
|
+ if (err)
|
|
|
+ goto err_neigh_init;
|
|
|
+
|
|
|
+ mlxsw_sp->fib_nb.notifier_call = mlxsw_sp_router_fib_event;
|
|
|
+ register_fib_notifier(&mlxsw_sp->fib_nb);
|
|
|
+ return 0;
|
|
|
+
|
|
|
+err_neigh_init:
|
|
|
+ mlxsw_sp_vrs_fini(mlxsw_sp);
|
|
|
+err_vrs_init:
|
|
|
+ __mlxsw_sp_router_fini(mlxsw_sp);
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
|
|
|
+{
|
|
|
+ unregister_fib_notifier(&mlxsw_sp->fib_nb);
|
|
|
+ mlxsw_sp_neigh_fini(mlxsw_sp);
|
|
|
+ mlxsw_sp_vrs_fini(mlxsw_sp);
|
|
|
+ __mlxsw_sp_router_fini(mlxsw_sp);
|
|
|
+}
|