Commit 83d57826 authored by Ido Schimmel's avatar Ido Schimmel Committed by David S. Miller

mlxsw: spectrum_router: Refresh nexthop neighbour when it becomes dead

The driver tries to periodically refresh neighbours that are used to
reach nexthops. This is done by periodically calling neigh_event_send().

However, if the neighbour becomes dead, there is nothing we can do to
return it to a connected state and the above function call is basically
a NOP.

This results in the nexthop never being written to the device's
adjacency table and therefore never used to forward packets.

Fix this by dropping our reference from the dead neighbour and
associating the nexthop with a new neigbhour which we will try to
refresh.

Fixes: a7ff87ac ("mlxsw: spectrum_router: Implement next-hop routing")
Signed-off-by: default avatarIdo Schimmel <idosch@mellanox.com>
Reported-by: default avatarAlex Veber <alexve@mellanox.com>
Tested-by: default avatarAlex Veber <alexve@mellanox.com>
Acked-by: default avatarJiri Pirko <jiri@mellanox.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent ee02c269
......@@ -2363,7 +2363,7 @@ static void mlxsw_sp_router_probe_unresolved_nexthops(struct work_struct *work)
static void
mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_neigh_entry *neigh_entry,
bool removing);
bool removing, bool dead);
static enum mlxsw_reg_rauht_op mlxsw_sp_rauht_op(bool adding)
{
......@@ -2507,7 +2507,8 @@ static void mlxsw_sp_router_neigh_event_work(struct work_struct *work)
memcpy(neigh_entry->ha, ha, ETH_ALEN);
mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, entry_connected);
mlxsw_sp_nexthop_neigh_update(mlxsw_sp, neigh_entry, !entry_connected);
mlxsw_sp_nexthop_neigh_update(mlxsw_sp, neigh_entry, !entry_connected,
dead);
if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
......@@ -3472,13 +3473,79 @@ static void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh,
nh->update = 1;
}
static int
mlxsw_sp_nexthop_dead_neigh_replace(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_neigh_entry *neigh_entry)
{
struct neighbour *n, *old_n = neigh_entry->key.n;
struct mlxsw_sp_nexthop *nh;
bool entry_connected;
u8 nud_state, dead;
int err;
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);
if (!n) {
n = neigh_create(nh->nh_grp->neigh_tbl, &nh->gw_addr,
nh->rif->dev);
if (IS_ERR(n))
return PTR_ERR(n);
neigh_event_send(n, NULL);
}
mlxsw_sp_neigh_entry_remove(mlxsw_sp, neigh_entry);
neigh_entry->key.n = n;
err = mlxsw_sp_neigh_entry_insert(mlxsw_sp, neigh_entry);
if (err)
goto err_neigh_entry_insert;
read_lock_bh(&n->lock);
nud_state = n->nud_state;
dead = n->dead;
read_unlock_bh(&n->lock);
entry_connected = nud_state & NUD_VALID && !dead;
list_for_each_entry(nh, &neigh_entry->nexthop_list,
neigh_list_node) {
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);
}
neigh_release(n);
return 0;
err_neigh_entry_insert:
neigh_entry->key.n = old_n;
mlxsw_sp_neigh_entry_insert(mlxsw_sp, neigh_entry);
neigh_release(n);
return err;
}
static void
mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_neigh_entry *neigh_entry,
bool removing)
bool removing, bool dead)
{
struct mlxsw_sp_nexthop *nh;
if (list_empty(&neigh_entry->nexthop_list))
return;
if (dead) {
int err;
err = mlxsw_sp_nexthop_dead_neigh_replace(mlxsw_sp,
neigh_entry);
if (err)
dev_err(mlxsw_sp->bus_info->dev, "Failed to replace dead neigh\n");
return;
}
list_for_each_entry(nh, &neigh_entry->nexthop_list,
neigh_list_node) {
__mlxsw_sp_nexthop_neigh_update(nh, removing);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment