aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6
diff options
context:
space:
mode:
authorEric Dumazet <eric.dumazet@gmail.com>2010-04-08 19:03:29 -0400
committerDavid S. Miller <davem@davemloft.net>2010-04-13 04:41:33 -0400
commitb6c6712a42ca3f9fa7f4a3d7c40e3a9dd1fd9e03 (patch)
tree42032b4978874e8ffcf6c851d13324b8c8c7c113 /net/ipv6
parent7a161ea92471087a1579239d7a58dd06eaa5601c (diff)
net: sk_dst_cache RCUification
With latest CONFIG_PROVE_RCU stuff, I felt more comfortable to make this work. sk->sk_dst_cache is currently protected by a rwlock (sk_dst_lock) This rwlock is readlocked for a very small amount of time, and dst entries are already freed after RCU grace period. This calls for RCU again :) This patch converts sk_dst_lock to a spinlock, and use RCU for readers. __sk_dst_get() is supposed to be called with rcu_read_lock() or if socket locked by user, so use appropriate rcu_dereference_check() condition (rcu_read_lock_held() || sock_owned_by_user(sk)) This patch avoids two atomic ops per tx packet on UDP connected sockets, for example, and permits sk_dst_lock to be much less dirtied. Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6')
-rw-r--r--net/ipv6/ipv6_sockglue.c25
1 files changed, 13 insertions, 12 deletions
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 33f60fca7aa7..1160400e9dbd 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -114,9 +114,9 @@ struct ipv6_txoptions *ipv6_update_options(struct sock *sk,
114 } 114 }
115 opt = xchg(&inet6_sk(sk)->opt, opt); 115 opt = xchg(&inet6_sk(sk)->opt, opt);
116 } else { 116 } else {
117 write_lock(&sk->sk_dst_lock); 117 spin_lock(&sk->sk_dst_lock);
118 opt = xchg(&inet6_sk(sk)->opt, opt); 118 opt = xchg(&inet6_sk(sk)->opt, opt);
119 write_unlock(&sk->sk_dst_lock); 119 spin_unlock(&sk->sk_dst_lock);
120 } 120 }
121 sk_dst_reset(sk); 121 sk_dst_reset(sk);
122 122
@@ -971,14 +971,13 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
971 case IPV6_MTU: 971 case IPV6_MTU:
972 { 972 {
973 struct dst_entry *dst; 973 struct dst_entry *dst;
974
974 val = 0; 975 val = 0;
975 lock_sock(sk); 976 rcu_read_lock();
976 dst = sk_dst_get(sk); 977 dst = __sk_dst_get(sk);
977 if (dst) { 978 if (dst)
978 val = dst_mtu(dst); 979 val = dst_mtu(dst);
979 dst_release(dst); 980 rcu_read_unlock();
980 }
981 release_sock(sk);
982 if (!val) 981 if (!val)
983 return -ENOTCONN; 982 return -ENOTCONN;
984 break; 983 break;
@@ -1066,12 +1065,14 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
1066 else 1065 else
1067 val = np->mcast_hops; 1066 val = np->mcast_hops;
1068 1067
1069 dst = sk_dst_get(sk); 1068 if (val < 0) {
1070 if (dst) { 1069 rcu_read_lock();
1071 if (val < 0) 1070 dst = __sk_dst_get(sk);
1071 if (dst)
1072 val = ip6_dst_hoplimit(dst); 1072 val = ip6_dst_hoplimit(dst);
1073 dst_release(dst); 1073 rcu_read_unlock();
1074 } 1074 }
1075
1075 if (val < 0) 1076 if (val < 0)
1076 val = sock_net(sk)->ipv6.devconf_all->hop_limit; 1077 val = sock_net(sk)->ipv6.devconf_all->hop_limit;
1077 break; 1078 break;