aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/ip6_tunnel.c
diff options
context:
space:
mode:
authorEric Dumazet <eric.dumazet@gmail.com>2010-09-15 16:25:34 -0400
committerDavid S. Miller <davem@davemloft.net>2010-09-17 00:58:44 -0400
commit94767632623c7bf5b16a0cf963ec93a8ad9acca4 (patch)
tree2d6f3328ac632de5b4cd5c35a79a8bc270f091fe /net/ipv6/ip6_tunnel.c
parentcaeda9b926c608702c99f3432aae2c24298c3c1d (diff)
ip6tnl: get rid of ip6_tnl_lock
As RTNL is held while doing tunnels inserts and deletes, we can remove ip6_tnl_lock spinlock. My initial RCU conversion was conservative and converted the rwlock to spinlock, with no RTNL requirement. Use appropriate rcu annotations and modern lockdep checks as well. Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6/ip6_tunnel.c')
-rw-r--r--net/ipv6/ip6_tunnel.c58
1 files changed, 28 insertions, 30 deletions
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index 29f99dd75bc6..9289cecac4de 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -83,15 +83,14 @@ struct ip6_tnl_net {
83 /* the IPv6 tunnel fallback device */ 83 /* the IPv6 tunnel fallback device */
84 struct net_device *fb_tnl_dev; 84 struct net_device *fb_tnl_dev;
85 /* lists for storing tunnels in use */ 85 /* lists for storing tunnels in use */
86 struct ip6_tnl *tnls_r_l[HASH_SIZE]; 86 struct ip6_tnl __rcu *tnls_r_l[HASH_SIZE];
87 struct ip6_tnl *tnls_wc[1]; 87 struct ip6_tnl __rcu *tnls_wc[1];
88 struct ip6_tnl **tnls[2]; 88 struct ip6_tnl __rcu **tnls[2];
89}; 89};
90 90
91/* 91/*
92 * Locking : hash tables are protected by RCU and a spinlock 92 * Locking : hash tables are protected by RCU and RTNL
93 */ 93 */
94static DEFINE_SPINLOCK(ip6_tnl_lock);
95 94
96static inline struct dst_entry *ip6_tnl_dst_check(struct ip6_tnl *t) 95static inline struct dst_entry *ip6_tnl_dst_check(struct ip6_tnl *t)
97{ 96{
@@ -138,8 +137,8 @@ static inline void ip6_tnl_dst_store(struct ip6_tnl *t, struct dst_entry *dst)
138static struct ip6_tnl * 137static struct ip6_tnl *
139ip6_tnl_lookup(struct net *net, struct in6_addr *remote, struct in6_addr *local) 138ip6_tnl_lookup(struct net *net, struct in6_addr *remote, struct in6_addr *local)
140{ 139{
141 unsigned h0 = HASH(remote); 140 unsigned int h0 = HASH(remote);
142 unsigned h1 = HASH(local); 141 unsigned int h1 = HASH(local);
143 struct ip6_tnl *t; 142 struct ip6_tnl *t;
144 struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); 143 struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
145 144
@@ -167,7 +166,7 @@ ip6_tnl_lookup(struct net *net, struct in6_addr *remote, struct in6_addr *local)
167 * Return: head of IPv6 tunnel list 166 * Return: head of IPv6 tunnel list
168 **/ 167 **/
169 168
170static struct ip6_tnl ** 169static struct ip6_tnl __rcu **
171ip6_tnl_bucket(struct ip6_tnl_net *ip6n, struct ip6_tnl_parm *p) 170ip6_tnl_bucket(struct ip6_tnl_net *ip6n, struct ip6_tnl_parm *p)
172{ 171{
173 struct in6_addr *remote = &p->raddr; 172 struct in6_addr *remote = &p->raddr;
@@ -190,12 +189,10 @@ ip6_tnl_bucket(struct ip6_tnl_net *ip6n, struct ip6_tnl_parm *p)
190static void 189static void
191ip6_tnl_link(struct ip6_tnl_net *ip6n, struct ip6_tnl *t) 190ip6_tnl_link(struct ip6_tnl_net *ip6n, struct ip6_tnl *t)
192{ 191{
193 struct ip6_tnl **tp = ip6_tnl_bucket(ip6n, &t->parms); 192 struct ip6_tnl __rcu **tp = ip6_tnl_bucket(ip6n, &t->parms);
194 193
195 spin_lock_bh(&ip6_tnl_lock); 194 rcu_assign_pointer(t->next , rtnl_dereference(*tp));
196 t->next = *tp;
197 rcu_assign_pointer(*tp, t); 195 rcu_assign_pointer(*tp, t);
198 spin_unlock_bh(&ip6_tnl_lock);
199} 196}
200 197
201/** 198/**
@@ -206,13 +203,14 @@ ip6_tnl_link(struct ip6_tnl_net *ip6n, struct ip6_tnl *t)
206static void 203static void
207ip6_tnl_unlink(struct ip6_tnl_net *ip6n, struct ip6_tnl *t) 204ip6_tnl_unlink(struct ip6_tnl_net *ip6n, struct ip6_tnl *t)
208{ 205{
209 struct ip6_tnl **tp; 206 struct ip6_tnl __rcu **tp;
210 207 struct ip6_tnl *iter;
211 for (tp = ip6_tnl_bucket(ip6n, &t->parms); *tp; tp = &(*tp)->next) { 208
212 if (t == *tp) { 209 for (tp = ip6_tnl_bucket(ip6n, &t->parms);
213 spin_lock_bh(&ip6_tnl_lock); 210 (iter = rtnl_dereference(*tp)) != NULL;
214 *tp = t->next; 211 tp = &iter->next) {
215 spin_unlock_bh(&ip6_tnl_lock); 212 if (t == iter) {
213 rcu_assign_pointer(*tp, t->next);
216 break; 214 break;
217 } 215 }
218 } 216 }
@@ -290,10 +288,13 @@ static struct ip6_tnl *ip6_tnl_locate(struct net *net,
290{ 288{
291 struct in6_addr *remote = &p->raddr; 289 struct in6_addr *remote = &p->raddr;
292 struct in6_addr *local = &p->laddr; 290 struct in6_addr *local = &p->laddr;
291 struct ip6_tnl __rcu **tp;
293 struct ip6_tnl *t; 292 struct ip6_tnl *t;
294 struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); 293 struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
295 294
296 for (t = *ip6_tnl_bucket(ip6n, p); t; t = t->next) { 295 for (tp = ip6_tnl_bucket(ip6n, p);
296 (t = rtnl_dereference(*tp)) != NULL;
297 tp = &t->next) {
297 if (ipv6_addr_equal(local, &t->parms.laddr) && 298 if (ipv6_addr_equal(local, &t->parms.laddr) &&
298 ipv6_addr_equal(remote, &t->parms.raddr)) 299 ipv6_addr_equal(remote, &t->parms.raddr))
299 return t; 300 return t;
@@ -318,13 +319,10 @@ ip6_tnl_dev_uninit(struct net_device *dev)
318 struct net *net = dev_net(dev); 319 struct net *net = dev_net(dev);
319 struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); 320 struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
320 321
321 if (dev == ip6n->fb_tnl_dev) { 322 if (dev == ip6n->fb_tnl_dev)
322 spin_lock_bh(&ip6_tnl_lock); 323 rcu_assign_pointer(ip6n->tnls_wc[0], NULL);
323 ip6n->tnls_wc[0] = NULL; 324 else
324 spin_unlock_bh(&ip6_tnl_lock);
325 } else {
326 ip6_tnl_unlink(ip6n, t); 325 ip6_tnl_unlink(ip6n, t);
327 }
328 ip6_tnl_dst_reset(t); 326 ip6_tnl_dst_reset(t);
329 dev_put(dev); 327 dev_put(dev);
330} 328}
@@ -1369,7 +1367,7 @@ static void __net_init ip6_fb_tnl_dev_init(struct net_device *dev)
1369 ip6_tnl_dev_init_gen(dev); 1367 ip6_tnl_dev_init_gen(dev);
1370 t->parms.proto = IPPROTO_IPV6; 1368 t->parms.proto = IPPROTO_IPV6;
1371 dev_hold(dev); 1369 dev_hold(dev);
1372 ip6n->tnls_wc[0] = t; 1370 rcu_assign_pointer(ip6n->tnls_wc[0], t);
1373} 1371}
1374 1372
1375static struct xfrm6_tunnel ip4ip6_handler __read_mostly = { 1373static struct xfrm6_tunnel ip4ip6_handler __read_mostly = {
@@ -1391,14 +1389,14 @@ static void __net_exit ip6_tnl_destroy_tunnels(struct ip6_tnl_net *ip6n)
1391 LIST_HEAD(list); 1389 LIST_HEAD(list);
1392 1390
1393 for (h = 0; h < HASH_SIZE; h++) { 1391 for (h = 0; h < HASH_SIZE; h++) {
1394 t = ip6n->tnls_r_l[h]; 1392 t = rtnl_dereference(ip6n->tnls_r_l[h]);
1395 while (t != NULL) { 1393 while (t != NULL) {
1396 unregister_netdevice_queue(t->dev, &list); 1394 unregister_netdevice_queue(t->dev, &list);
1397 t = t->next; 1395 t = rtnl_dereference(t->next);
1398 } 1396 }
1399 } 1397 }
1400 1398
1401 t = ip6n->tnls_wc[0]; 1399 t = rtnl_dereference(ip6n->tnls_wc[0]);
1402 unregister_netdevice_queue(t->dev, &list); 1400 unregister_netdevice_queue(t->dev, &list);
1403 unregister_netdevice_many(&list); 1401 unregister_netdevice_many(&list);
1404} 1402}