diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2010-04-08 19:03:29 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-04-13 04:41:33 -0400 |
commit | b6c6712a42ca3f9fa7f4a3d7c40e3a9dd1fd9e03 (patch) | |
tree | 42032b4978874e8ffcf6c851d13324b8c8c7c113 /net/ipv6 | |
parent | 7a161ea92471087a1579239d7a58dd06eaa5601c (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.c | 25 |
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; |