Unlike with IPv[46], where "ip_finish_output2" triggers a refresh of STALE neighbour entries via "neigh_output", "rdma_resolve_addr" never triggers an update. If a wrong STALE entry ever enters the cache, it'll remain wrong forever (unless refreshed via TCP/IP, or otherwise). Let the cache inconsistency resolve itself by triggering an update from "rdma_resolve_addr". Signed-off-by: Gerd Rausch <gerd.rausch@xxxxxxxxxx> --- v2: Add a "(__force u32)" cast for "__ipv4_neigh_lookup_noref" drivers/infiniband/core/addr.c | 40 ++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c index f253295795f0..704dc9cc130e 100644 --- a/drivers/infiniband/core/addr.c +++ b/drivers/infiniband/core/addr.c @@ -394,6 +394,8 @@ static int addr4_resolve(struct sockaddr *src_sock, __be32 dst_ip = dst_in->sin_addr.s_addr; struct rtable *rt; struct flowi4 fl4; + struct net_device *dev; + struct neighbour *neigh; int ret; memset(&fl4, 0, sizeof(fl4)); @@ -409,6 +411,24 @@ static int addr4_resolve(struct sockaddr *src_sock, addr->hoplimit = ip4_dst_hoplimit(&rt->dst); + /* trigger ARP-entry refresh if necessary, + * the same way "ip_finish_output2" does + */ + if (addr->bound_dev_if) { + dev = dev_get_by_index(addr->net, addr->bound_dev_if); + } else { + dev = rt->dst.dev; + dev_hold(dev); + } + if (dev) { + rcu_read_lock_bh(); + neigh = __ipv4_neigh_lookup_noref(dev, (__force u32)dst_ip); + if (neigh) + neigh_event_send(neigh, NULL); + rcu_read_unlock_bh(); + dev_put(dev); + } + *prt = rt; return 0; } @@ -424,6 +444,8 @@ static int addr6_resolve(struct sockaddr *src_sock, (const struct sockaddr_in6 *)dst_sock; struct flowi6 fl6; struct dst_entry *dst; + struct net_device *dev; + struct neighbour *neigh; memset(&fl6, 0, sizeof fl6); fl6.daddr = dst_in->sin6_addr; @@ -439,6 +461,24 @@ static int addr6_resolve(struct sockaddr *src_sock, addr->hoplimit = ip6_dst_hoplimit(dst); + /* trigger neighbour-entry refresh if necessary, + * the same way "ip6_finish_output2" does + */ + if (addr->bound_dev_if) { + dev = dev_get_by_index(addr->net, addr->bound_dev_if); + } else { + dev = dst->dev; + dev_hold(dev); + } + if (dev) { + rcu_read_lock_bh(); + neigh = __ipv6_neigh_lookup_noref(dst->dev, &dst_in->sin6_addr); + if (neigh) + neigh_event_send(neigh, NULL); + rcu_read_unlock_bh(); + dev_put(dev); + } + *pdst = dst; return 0; }