Commit dcdfdf56 authored by Alexei Starovoitov's avatar Alexei Starovoitov Committed by David S. Miller

ipv4: fix race in concurrent ip_route_input_slow()

CPUs can ask for local route via ip_route_input_noref() concurrently.
if nh_rth_input is not cached yet, CPUs will proceed to allocate
equivalent DSTs on 'lo' and then will try to cache them in nh_rth_input
via rt_cache_route()
Most of the time they succeed, but on occasion the following two lines:
	orig = *p;
	prev = cmpxchg(p, orig, rt);
in rt_cache_route() do race and one of the cpus fails to complete cmpxchg.
But ip_route_input_slow() doesn't check the return code of rt_cache_route(),
so dst is leaking. dst_destroy() is never called and 'lo' device
refcnt doesn't go to zero, which can be seen in the logs as:
	unregister_netdevice: waiting for lo to become free. Usage count = 1
Adding mdelay() between above two lines makes it easily reproducible.
Fix it similar to nh_pcpu_rth_output case.

Fixes: d2d68ba9 ("ipv4: Cache input routes in fib_info nexthops.")
Signed-off-by: default avatarAlexei Starovoitov <ast@plumgrid.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 4f837c3b
...@@ -1776,8 +1776,12 @@ out: return err; ...@@ -1776,8 +1776,12 @@ out: return err;
rth->dst.error= -err; rth->dst.error= -err;
rth->rt_flags &= ~RTCF_LOCAL; rth->rt_flags &= ~RTCF_LOCAL;
} }
if (do_cache) if (do_cache) {
rt_cache_route(&FIB_RES_NH(res), rth); if (unlikely(!rt_cache_route(&FIB_RES_NH(res), rth))) {
rth->dst.flags |= DST_NOCACHE;
rt_add_uncached_list(rth);
}
}
skb_dst_set(skb, &rth->dst); skb_dst_set(skb, &rth->dst);
err = 0; err = 0;
goto out; goto out;
......
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