aboutsummaryrefslogtreecommitdiffstats
path: root/include/net
diff options
context:
space:
mode:
authorEric Dumazet <eric.dumazet@gmail.com>2012-07-30 21:08:23 -0400
committerDavid S. Miller <davem@davemloft.net>2012-07-31 17:41:38 -0400
commit54764bb647b2e847c512acf8d443df965da35000 (patch)
tree3918f6f42679d0ebdcef230f28285f99d178be62 /include/net
parent5a0d513b622ee41e117fc37e26e27e8ef42e8dae (diff)
ipv4: Restore old dst_free() behavior.
commit 404e0a8b6a55 (net: ipv4: fix RCU races on dst refcounts) tried to solve a race but added a problem at device/fib dismantle time : We really want to call dst_free() as soon as possible, even if sockets still have dst in their cache. dst_release() calls in free_fib_info_rcu() are not welcomed. Root of the problem was that now we also cache output routes (in nh_rth_output), we must use call_rcu() instead of call_rcu_bh() in rt_free(), because output route lookups are done in process context. Based on feedback and initial patch from David Miller (adding another call_rcu_bh() call in fib, but it appears it was not the right fix) I left the inet_sk_rx_dst_set() helper and added __rcu attributes to nh_rth_output and nh_rth_input to better document what is going on in this code. Signed-off-by: Eric Dumazet <edumazet@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'include/net')
-rw-r--r--include/net/dst.h7
-rw-r--r--include/net/inet_sock.h10
-rw-r--r--include/net/ip_fib.h4
3 files changed, 11 insertions, 10 deletions
diff --git a/include/net/dst.h b/include/net/dst.h
index 31a9fd39edb6..baf597890064 100644
--- a/include/net/dst.h
+++ b/include/net/dst.h
@@ -61,7 +61,6 @@ struct dst_entry {
61#define DST_NOPEER 0x0040 61#define DST_NOPEER 0x0040
62#define DST_FAKE_RTABLE 0x0080 62#define DST_FAKE_RTABLE 0x0080
63#define DST_XFRM_TUNNEL 0x0100 63#define DST_XFRM_TUNNEL 0x0100
64#define DST_RCU_FREE 0x0200
65 64
66 unsigned short pending_confirm; 65 unsigned short pending_confirm;
67 66
@@ -383,6 +382,12 @@ static inline void dst_free(struct dst_entry *dst)
383 __dst_free(dst); 382 __dst_free(dst);
384} 383}
385 384
385static inline void dst_rcu_free(struct rcu_head *head)
386{
387 struct dst_entry *dst = container_of(head, struct dst_entry, rcu_head);
388 dst_free(dst);
389}
390
386static inline void dst_confirm(struct dst_entry *dst) 391static inline void dst_confirm(struct dst_entry *dst)
387{ 392{
388 dst->pending_confirm = 1; 393 dst->pending_confirm = 1;
diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h
index e3fd34c83ac9..83b567fe1941 100644
--- a/include/net/inet_sock.h
+++ b/include/net/inet_sock.h
@@ -253,13 +253,9 @@ static inline void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb
253{ 253{
254 struct dst_entry *dst = skb_dst(skb); 254 struct dst_entry *dst = skb_dst(skb);
255 255
256 if (atomic_inc_not_zero(&dst->__refcnt)) { 256 dst_hold(dst);
257 if (!(dst->flags & DST_RCU_FREE)) 257 sk->sk_rx_dst = dst;
258 dst->flags |= DST_RCU_FREE; 258 inet_sk(sk)->rx_dst_ifindex = skb->skb_iif;
259
260 sk->sk_rx_dst = dst;
261 inet_sk(sk)->rx_dst_ifindex = skb->skb_iif;
262 }
263} 259}
264 260
265#endif /* _INET_SOCK_H */ 261#endif /* _INET_SOCK_H */
diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h
index e69c3a47153d..e521a03515b1 100644
--- a/include/net/ip_fib.h
+++ b/include/net/ip_fib.h
@@ -81,8 +81,8 @@ struct fib_nh {
81 __be32 nh_gw; 81 __be32 nh_gw;
82 __be32 nh_saddr; 82 __be32 nh_saddr;
83 int nh_saddr_genid; 83 int nh_saddr_genid;
84 struct rtable *nh_rth_output; 84 struct rtable __rcu *nh_rth_output;
85 struct rtable *nh_rth_input; 85 struct rtable __rcu *nh_rth_input;
86 struct fnhe_hash_bucket *nh_exceptions; 86 struct fnhe_hash_bucket *nh_exceptions;
87}; 87};
88 88