diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2010-09-15 16:25:34 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-09-17 00:58:44 -0400 |
commit | 94767632623c7bf5b16a0cf963ec93a8ad9acca4 (patch) | |
tree | 2d6f3328ac632de5b4cd5c35a79a8bc270f091fe /net/ipv6/ip6_tunnel.c | |
parent | caeda9b926c608702c99f3432aae2c24298c3c1d (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.c | 58 |
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 | */ |
94 | static DEFINE_SPINLOCK(ip6_tnl_lock); | ||
95 | 94 | ||
96 | static inline struct dst_entry *ip6_tnl_dst_check(struct ip6_tnl *t) | 95 | static 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) | |||
138 | static struct ip6_tnl * | 137 | static struct ip6_tnl * |
139 | ip6_tnl_lookup(struct net *net, struct in6_addr *remote, struct in6_addr *local) | 138 | ip6_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 | ||
170 | static struct ip6_tnl ** | 169 | static struct ip6_tnl __rcu ** |
171 | ip6_tnl_bucket(struct ip6_tnl_net *ip6n, struct ip6_tnl_parm *p) | 170 | ip6_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) | |||
190 | static void | 189 | static void |
191 | ip6_tnl_link(struct ip6_tnl_net *ip6n, struct ip6_tnl *t) | 190 | ip6_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) | |||
206 | static void | 203 | static void |
207 | ip6_tnl_unlink(struct ip6_tnl_net *ip6n, struct ip6_tnl *t) | 204 | ip6_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 | ||
1375 | static struct xfrm6_tunnel ip4ip6_handler __read_mostly = { | 1373 | static 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 | } |