提交 a827d6c0 编写于 作者: I Ido Schimmel 提交者: Greg Kroah-Hartman

mlxsw: spectrum_router: Refresh nexthop neighbour when it becomes dead

[ Upstream commit 83d5782681cc12b3d485a83cb34c46b2445f510c ]

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: NIdo Schimmel <idosch@mellanox.com>
Reported-by: NAlex Veber <alexve@mellanox.com>
Tested-by: NAlex Veber <alexve@mellanox.com>
Acked-by: NJiri Pirko <jiri@mellanox.com>
Signed-off-by: NDavid S. Miller <davem@davemloft.net>
Signed-off-by: NSasha Levin <sashal@kernel.org>
上级 52600d00
...@@ -2228,7 +2228,7 @@ static void mlxsw_sp_router_probe_unresolved_nexthops(struct work_struct *work) ...@@ -2228,7 +2228,7 @@ static void mlxsw_sp_router_probe_unresolved_nexthops(struct work_struct *work)
static void static void
mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_neigh_entry *neigh_entry, 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) static enum mlxsw_reg_rauht_op mlxsw_sp_rauht_op(bool adding)
{ {
...@@ -2359,7 +2359,8 @@ static void mlxsw_sp_router_neigh_event_work(struct work_struct *work) ...@@ -2359,7 +2359,8 @@ static void mlxsw_sp_router_neigh_event_work(struct work_struct *work)
memcpy(neigh_entry->ha, ha, ETH_ALEN); memcpy(neigh_entry->ha, ha, ETH_ALEN);
mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, entry_connected); 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)) if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry); mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
...@@ -3323,13 +3324,79 @@ static void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh, ...@@ -3323,13 +3324,79 @@ static void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh,
nh->update = 1; 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 static void
mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_neigh_entry *neigh_entry, struct mlxsw_sp_neigh_entry *neigh_entry,
bool removing) bool removing, bool dead)
{ {
struct mlxsw_sp_nexthop *nh; 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, list_for_each_entry(nh, &neigh_entry->nexthop_list,
neigh_list_node) { neigh_list_node) {
__mlxsw_sp_nexthop_neigh_update(nh, removing); __mlxsw_sp_nexthop_neigh_update(nh, removing);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册