diff options
author | Eric Dumazet <edumazet@google.com> | 2012-07-29 19:20:37 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-07-30 17:53:22 -0400 |
commit | 404e0a8b6a55d5e1cd138c6deb1bca9abdf75d8c (patch) | |
tree | 38e9748d38c415cc97b973fecb9279cd43f76393 /net/ipv4/route.c | |
parent | cca32e4bf999a34ac08d959f351f2b30bcd02460 (diff) |
net: ipv4: fix RCU races on dst refcounts
commit c6cffba4ffa2 (ipv4: Fix input route performance regression.)
added various fatal races with dst refcounts.
crashes happen on tcp workloads if routes are added/deleted at the same
time.
The dst_free() calls from free_fib_info_rcu() are clearly racy.
We need instead regular dst refcounting (dst_release()) and make
sure dst_release() is aware of RCU grace periods :
Add DST_RCU_FREE flag so that dst_release() respects an RCU grace period
before dst destruction for cached dst
Introduce a new inet_sk_rx_dst_set() helper, using atomic_inc_not_zero()
to make sure we dont increase a zero refcount (On a dst currently
waiting an rcu grace period before destruction)
rt_cache_route() must take a reference on the new cached route, and
release it if was not able to install it.
With this patch, my machines survive various benchmarks.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/route.c')
-rw-r--r-- | net/ipv4/route.c | 16 |
1 files changed, 4 insertions, 12 deletions
diff --git a/net/ipv4/route.c b/net/ipv4/route.c index fc1a81ca79a7..d6eabcfe8a90 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c | |||
@@ -1199,11 +1199,6 @@ restart: | |||
1199 | fnhe->fnhe_stamp = jiffies; | 1199 | fnhe->fnhe_stamp = jiffies; |
1200 | } | 1200 | } |
1201 | 1201 | ||
1202 | static inline void rt_free(struct rtable *rt) | ||
1203 | { | ||
1204 | call_rcu_bh(&rt->dst.rcu_head, dst_rcu_free); | ||
1205 | } | ||
1206 | |||
1207 | static void rt_cache_route(struct fib_nh *nh, struct rtable *rt) | 1202 | static void rt_cache_route(struct fib_nh *nh, struct rtable *rt) |
1208 | { | 1203 | { |
1209 | struct rtable *orig, *prev, **p = &nh->nh_rth_output; | 1204 | struct rtable *orig, *prev, **p = &nh->nh_rth_output; |
@@ -1213,17 +1208,14 @@ static void rt_cache_route(struct fib_nh *nh, struct rtable *rt) | |||
1213 | 1208 | ||
1214 | orig = *p; | 1209 | orig = *p; |
1215 | 1210 | ||
1211 | rt->dst.flags |= DST_RCU_FREE; | ||
1212 | dst_hold(&rt->dst); | ||
1216 | prev = cmpxchg(p, orig, rt); | 1213 | prev = cmpxchg(p, orig, rt); |
1217 | if (prev == orig) { | 1214 | if (prev == orig) { |
1218 | if (orig) | 1215 | if (orig) |
1219 | rt_free(orig); | 1216 | dst_release(&orig->dst); |
1220 | } else { | 1217 | } else { |
1221 | /* Routes we intend to cache in the FIB nexthop have | 1218 | dst_release(&rt->dst); |
1222 | * the DST_NOCACHE bit clear. However, if we are | ||
1223 | * unsuccessful at storing this route into the cache | ||
1224 | * we really need to set it. | ||
1225 | */ | ||
1226 | rt->dst.flags |= DST_NOCACHE; | ||
1227 | } | 1219 | } |
1228 | } | 1220 | } |
1229 | 1221 | ||