|
@@ -594,21 +594,22 @@ static int mlxsw_sp_vrs_init(struct mlxsw_sp *mlxsw_sp)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp);
|
|
|
+
|
|
|
static void mlxsw_sp_vrs_fini(struct mlxsw_sp *mlxsw_sp)
|
|
|
{
|
|
|
+ mlxsw_sp_router_fib_flush(mlxsw_sp);
|
|
|
kfree(mlxsw_sp->router.vrs);
|
|
|
}
|
|
|
|
|
|
struct mlxsw_sp_neigh_key {
|
|
|
- unsigned char addr[sizeof(struct in6_addr)];
|
|
|
- struct net_device *dev;
|
|
|
+ struct neighbour *n;
|
|
|
};
|
|
|
|
|
|
struct mlxsw_sp_neigh_entry {
|
|
|
struct rhash_head ht_node;
|
|
|
struct mlxsw_sp_neigh_key key;
|
|
|
u16 rif;
|
|
|
- struct neighbour *n;
|
|
|
bool offloaded;
|
|
|
struct delayed_work dw;
|
|
|
struct mlxsw_sp_port *mlxsw_sp_port;
|
|
@@ -646,19 +647,15 @@ mlxsw_sp_neigh_entry_remove(struct mlxsw_sp *mlxsw_sp,
|
|
|
static void mlxsw_sp_router_neigh_update_hw(struct work_struct *work);
|
|
|
|
|
|
static struct mlxsw_sp_neigh_entry *
|
|
|
-mlxsw_sp_neigh_entry_create(const void *addr, size_t addr_len,
|
|
|
- struct net_device *dev, u16 rif,
|
|
|
- struct neighbour *n)
|
|
|
+mlxsw_sp_neigh_entry_create(struct neighbour *n, u16 rif)
|
|
|
{
|
|
|
struct mlxsw_sp_neigh_entry *neigh_entry;
|
|
|
|
|
|
neigh_entry = kzalloc(sizeof(*neigh_entry), GFP_ATOMIC);
|
|
|
if (!neigh_entry)
|
|
|
return NULL;
|
|
|
- memcpy(neigh_entry->key.addr, addr, addr_len);
|
|
|
- neigh_entry->key.dev = dev;
|
|
|
+ neigh_entry->key.n = n;
|
|
|
neigh_entry->rif = rif;
|
|
|
- neigh_entry->n = n;
|
|
|
INIT_DELAYED_WORK(&neigh_entry->dw, mlxsw_sp_router_neigh_update_hw);
|
|
|
INIT_LIST_HEAD(&neigh_entry->nexthop_list);
|
|
|
return neigh_entry;
|
|
@@ -671,13 +668,11 @@ mlxsw_sp_neigh_entry_destroy(struct mlxsw_sp_neigh_entry *neigh_entry)
|
|
|
}
|
|
|
|
|
|
static struct mlxsw_sp_neigh_entry *
|
|
|
-mlxsw_sp_neigh_entry_lookup(struct mlxsw_sp *mlxsw_sp, const void *addr,
|
|
|
- size_t addr_len, struct net_device *dev)
|
|
|
+mlxsw_sp_neigh_entry_lookup(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
|
|
|
{
|
|
|
- struct mlxsw_sp_neigh_key key = {{ 0 } };
|
|
|
+ struct mlxsw_sp_neigh_key key;
|
|
|
|
|
|
- memcpy(key.addr, addr, addr_len);
|
|
|
- key.dev = dev;
|
|
|
+ key.n = n;
|
|
|
return rhashtable_lookup_fast(&mlxsw_sp->router.neigh_ht,
|
|
|
&key, mlxsw_sp_neigh_ht_params);
|
|
|
}
|
|
@@ -689,26 +684,20 @@ int mlxsw_sp_router_neigh_construct(struct net_device *dev,
|
|
|
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
|
|
|
struct mlxsw_sp_neigh_entry *neigh_entry;
|
|
|
struct mlxsw_sp_rif *r;
|
|
|
- u32 dip;
|
|
|
int err;
|
|
|
|
|
|
if (n->tbl != &arp_tbl)
|
|
|
return 0;
|
|
|
|
|
|
- dip = ntohl(*((__be32 *) n->primary_key));
|
|
|
- neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, &dip, sizeof(dip),
|
|
|
- n->dev);
|
|
|
- if (neigh_entry) {
|
|
|
- WARN_ON(neigh_entry->n != n);
|
|
|
+ neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
|
|
|
+ if (neigh_entry)
|
|
|
return 0;
|
|
|
- }
|
|
|
|
|
|
r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, n->dev);
|
|
|
if (WARN_ON(!r))
|
|
|
return -EINVAL;
|
|
|
|
|
|
- neigh_entry = mlxsw_sp_neigh_entry_create(&dip, sizeof(dip), n->dev,
|
|
|
- r->rif, n);
|
|
|
+ neigh_entry = mlxsw_sp_neigh_entry_create(n, r->rif);
|
|
|
if (!neigh_entry)
|
|
|
return -ENOMEM;
|
|
|
err = mlxsw_sp_neigh_entry_insert(mlxsw_sp, neigh_entry);
|
|
@@ -727,14 +716,11 @@ void mlxsw_sp_router_neigh_destroy(struct net_device *dev,
|
|
|
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
|
|
|
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
|
|
|
struct mlxsw_sp_neigh_entry *neigh_entry;
|
|
|
- u32 dip;
|
|
|
|
|
|
if (n->tbl != &arp_tbl)
|
|
|
return;
|
|
|
|
|
|
- dip = ntohl(*((__be32 *) n->primary_key));
|
|
|
- neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, &dip, sizeof(dip),
|
|
|
- n->dev);
|
|
|
+ neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
|
|
|
if (!neigh_entry)
|
|
|
return;
|
|
|
mlxsw_sp_neigh_entry_remove(mlxsw_sp, neigh_entry);
|
|
@@ -817,6 +803,26 @@ static void mlxsw_sp_router_neigh_rec_process(struct mlxsw_sp *mlxsw_sp,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static bool mlxsw_sp_router_rauhtd_is_full(char *rauhtd_pl)
|
|
|
+{
|
|
|
+ u8 num_rec, last_rec_index, num_entries;
|
|
|
+
|
|
|
+ num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
|
|
|
+ last_rec_index = num_rec - 1;
|
|
|
+
|
|
|
+ if (num_rec < MLXSW_REG_RAUHTD_REC_MAX_NUM)
|
|
|
+ return false;
|
|
|
+ if (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, last_rec_index) ==
|
|
|
+ MLXSW_REG_RAUHTD_TYPE_IPV6)
|
|
|
+ return true;
|
|
|
+
|
|
|
+ num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
|
|
|
+ last_rec_index);
|
|
|
+ if (++num_entries == MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC)
|
|
|
+ return true;
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
static int mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp)
|
|
|
{
|
|
|
char *rauhtd_pl;
|
|
@@ -843,7 +849,7 @@ static int mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp)
|
|
|
for (i = 0; i < num_rec; i++)
|
|
|
mlxsw_sp_router_neigh_rec_process(mlxsw_sp, rauhtd_pl,
|
|
|
i);
|
|
|
- } while (num_rec);
|
|
|
+ } while (mlxsw_sp_router_rauhtd_is_full(rauhtd_pl));
|
|
|
rtnl_unlock();
|
|
|
|
|
|
kfree(rauhtd_pl);
|
|
@@ -862,7 +868,7 @@ static void mlxsw_sp_router_neighs_update_nh(struct mlxsw_sp *mlxsw_sp)
|
|
|
* is active regardless of the traffic.
|
|
|
*/
|
|
|
if (!list_empty(&neigh_entry->nexthop_list))
|
|
|
- neigh_event_send(neigh_entry->n, NULL);
|
|
|
+ neigh_event_send(neigh_entry->key.n, NULL);
|
|
|
}
|
|
|
rtnl_unlock();
|
|
|
}
|
|
@@ -908,9 +914,9 @@ static void mlxsw_sp_router_probe_unresolved_nexthops(struct work_struct *work)
|
|
|
rtnl_lock();
|
|
|
list_for_each_entry(neigh_entry, &mlxsw_sp->router.nexthop_neighs_list,
|
|
|
nexthop_neighs_list_node) {
|
|
|
- if (!(neigh_entry->n->nud_state & NUD_VALID) &&
|
|
|
+ if (!(neigh_entry->key.n->nud_state & NUD_VALID) &&
|
|
|
!list_empty(&neigh_entry->nexthop_list))
|
|
|
- neigh_event_send(neigh_entry->n, NULL);
|
|
|
+ neigh_event_send(neigh_entry->key.n, NULL);
|
|
|
}
|
|
|
rtnl_unlock();
|
|
|
|
|
@@ -927,7 +933,7 @@ static void mlxsw_sp_router_neigh_update_hw(struct work_struct *work)
|
|
|
{
|
|
|
struct mlxsw_sp_neigh_entry *neigh_entry =
|
|
|
container_of(work, struct mlxsw_sp_neigh_entry, dw.work);
|
|
|
- struct neighbour *n = neigh_entry->n;
|
|
|
+ struct neighbour *n = neigh_entry->key.n;
|
|
|
struct mlxsw_sp_port *mlxsw_sp_port = neigh_entry->mlxsw_sp_port;
|
|
|
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
|
|
|
char rauht_pl[MLXSW_REG_RAUHT_LEN];
|
|
@@ -1030,11 +1036,8 @@ int mlxsw_sp_router_netevent_event(struct notifier_block *unused,
|
|
|
|
|
|
mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
|
|
|
dip = ntohl(*((__be32 *) n->primary_key));
|
|
|
- neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp,
|
|
|
- &dip,
|
|
|
- sizeof(__be32),
|
|
|
- dev);
|
|
|
- if (WARN_ON(!neigh_entry) || WARN_ON(neigh_entry->n != n)) {
|
|
|
+ neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
|
|
|
+ if (WARN_ON(!neigh_entry)) {
|
|
|
mlxsw_sp_port_dev_put(mlxsw_sp_port);
|
|
|
return NOTIFY_DONE;
|
|
|
}
|
|
@@ -1343,33 +1346,26 @@ static int mlxsw_sp_nexthop_init(struct mlxsw_sp *mlxsw_sp,
|
|
|
struct fib_nh *fib_nh)
|
|
|
{
|
|
|
struct mlxsw_sp_neigh_entry *neigh_entry;
|
|
|
- u32 gwip = ntohl(fib_nh->nh_gw);
|
|
|
struct net_device *dev = fib_nh->nh_dev;
|
|
|
struct neighbour *n;
|
|
|
u8 nud_state;
|
|
|
|
|
|
- neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, &gwip,
|
|
|
- sizeof(gwip), dev);
|
|
|
- if (!neigh_entry) {
|
|
|
- __be32 gwipn = htonl(gwip);
|
|
|
-
|
|
|
- n = neigh_create(&arp_tbl, &gwipn, dev);
|
|
|
+ /* Take a reference of neigh here ensuring that neigh would
|
|
|
+ * not be detructed before the nexthop entry is finished.
|
|
|
+ * The reference is taken either in neigh_lookup() or
|
|
|
+ * in neith_create() in case n is not found.
|
|
|
+ */
|
|
|
+ n = neigh_lookup(&arp_tbl, &fib_nh->nh_gw, dev);
|
|
|
+ if (!n) {
|
|
|
+ n = neigh_create(&arp_tbl, &fib_nh->nh_gw, dev);
|
|
|
if (IS_ERR(n))
|
|
|
return PTR_ERR(n);
|
|
|
neigh_event_send(n, NULL);
|
|
|
- neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, &gwip,
|
|
|
- sizeof(gwip), dev);
|
|
|
- if (!neigh_entry) {
|
|
|
- neigh_release(n);
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
- } else {
|
|
|
- /* Take a reference of neigh here ensuring that neigh would
|
|
|
- * not be detructed before the nexthop entry is finished.
|
|
|
- * The second branch takes the reference in neith_create()
|
|
|
- */
|
|
|
- n = neigh_entry->n;
|
|
|
- neigh_clone(n);
|
|
|
+ }
|
|
|
+ neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
|
|
|
+ if (!neigh_entry) {
|
|
|
+ neigh_release(n);
|
|
|
+ return -EINVAL;
|
|
|
}
|
|
|
|
|
|
/* If that is the first nexthop connected to that neigh, add to
|
|
@@ -1403,7 +1399,7 @@ static void mlxsw_sp_nexthop_fini(struct mlxsw_sp *mlxsw_sp,
|
|
|
if (list_empty(&nh->neigh_entry->nexthop_list))
|
|
|
list_del(&nh->neigh_entry->nexthop_neighs_list_node);
|
|
|
|
|
|
- neigh_release(neigh_entry->n);
|
|
|
+ neigh_release(neigh_entry->key.n);
|
|
|
}
|
|
|
|
|
|
static struct mlxsw_sp_nexthop_group *
|
|
@@ -1463,11 +1459,11 @@ static bool mlxsw_sp_nexthop_match(struct mlxsw_sp_nexthop *nh,
|
|
|
|
|
|
for (i = 0; i < fi->fib_nhs; i++) {
|
|
|
struct fib_nh *fib_nh = &fi->fib_nh[i];
|
|
|
- u32 gwip = ntohl(fib_nh->nh_gw);
|
|
|
+ struct neighbour *n = nh->neigh_entry->key.n;
|
|
|
|
|
|
- if (memcmp(nh->neigh_entry->key.addr,
|
|
|
- &gwip, sizeof(u32)) == 0 &&
|
|
|
- nh->neigh_entry->key.dev == fib_nh->nh_dev)
|
|
|
+ if (memcmp(n->primary_key, &fib_nh->nh_gw,
|
|
|
+ sizeof(fib_nh->nh_gw)) == 0 &&
|
|
|
+ n->dev == fib_nh->nh_dev)
|
|
|
return true;
|
|
|
}
|
|
|
return false;
|
|
@@ -1874,18 +1870,18 @@ static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp)
|
|
|
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
|
|
|
}
|
|
|
|
|
|
-static void mlxsw_sp_router_fib4_abort(struct mlxsw_sp *mlxsw_sp)
|
|
|
+static void mlxsw_sp_router_fib_flush(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;
|
|
|
|
|
@@ -1901,6 +1897,13 @@ static void mlxsw_sp_router_fib4_abort(struct mlxsw_sp *mlxsw_sp)
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
+}
|
|
|
+
|
|
|
+static void mlxsw_sp_router_fib4_abort(struct mlxsw_sp *mlxsw_sp)
|
|
|
+{
|
|
|
+ int err;
|
|
|
+
|
|
|
+ mlxsw_sp_router_fib_flush(mlxsw_sp);
|
|
|
mlxsw_sp->router.aborted = true;
|
|
|
err = mlxsw_sp_router_set_abort_trap(mlxsw_sp);
|
|
|
if (err)
|
|
@@ -1958,6 +1961,9 @@ static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
|
|
|
struct fib_entry_notifier_info *fen_info = ptr;
|
|
|
int err;
|
|
|
|
|
|
+ if (!net_eq(fen_info->info.net, &init_net))
|
|
|
+ return NOTIFY_DONE;
|
|
|
+
|
|
|
switch (event) {
|
|
|
case FIB_EVENT_ENTRY_ADD:
|
|
|
err = mlxsw_sp_router_fib4_add(mlxsw_sp, fen_info);
|