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>
This commit is contained in:
		
					parent
					
						
							
								caeda9b926
							
						
					
				
			
			
				commit
				
					
						9476763262
					
				
			
		
					 1 changed files with 27 additions and 29 deletions
				
			
		|  | @ -83,15 +83,14 @@ struct ip6_tnl_net { | ||||||
| 	/* the IPv6 tunnel fallback device */ | 	/* the IPv6 tunnel fallback device */ | ||||||
| 	struct net_device *fb_tnl_dev; | 	struct net_device *fb_tnl_dev; | ||||||
| 	/* lists for storing tunnels in use */ | 	/* lists for storing tunnels in use */ | ||||||
| 	struct ip6_tnl *tnls_r_l[HASH_SIZE]; | 	struct ip6_tnl __rcu *tnls_r_l[HASH_SIZE]; | ||||||
| 	struct ip6_tnl *tnls_wc[1]; | 	struct ip6_tnl __rcu *tnls_wc[1]; | ||||||
| 	struct ip6_tnl **tnls[2]; | 	struct ip6_tnl __rcu **tnls[2]; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  * Locking : hash tables are protected by RCU and a spinlock |  * Locking : hash tables are protected by RCU and RTNL | ||||||
|  */ |  */ | ||||||
| static DEFINE_SPINLOCK(ip6_tnl_lock); |  | ||||||
| 
 | 
 | ||||||
| static inline struct dst_entry *ip6_tnl_dst_check(struct ip6_tnl *t) | static inline struct dst_entry *ip6_tnl_dst_check(struct ip6_tnl *t) | ||||||
| { | { | ||||||
|  | @ -138,8 +137,8 @@ static inline void ip6_tnl_dst_store(struct ip6_tnl *t, struct dst_entry *dst) | ||||||
| static struct ip6_tnl * | static struct ip6_tnl * | ||||||
| ip6_tnl_lookup(struct net *net, struct in6_addr *remote, struct in6_addr *local) | ip6_tnl_lookup(struct net *net, struct in6_addr *remote, struct in6_addr *local) | ||||||
| { | { | ||||||
| 	unsigned h0 = HASH(remote); | 	unsigned int h0 = HASH(remote); | ||||||
| 	unsigned h1 = HASH(local); | 	unsigned int h1 = HASH(local); | ||||||
| 	struct ip6_tnl *t; | 	struct ip6_tnl *t; | ||||||
| 	struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); | 	struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); | ||||||
| 
 | 
 | ||||||
|  | @ -167,7 +166,7 @@ ip6_tnl_lookup(struct net *net, struct in6_addr *remote, struct in6_addr *local) | ||||||
|  * Return: head of IPv6 tunnel list |  * Return: head of IPv6 tunnel list | ||||||
|  **/ |  **/ | ||||||
| 
 | 
 | ||||||
| static struct ip6_tnl ** | static struct ip6_tnl __rcu ** | ||||||
| ip6_tnl_bucket(struct ip6_tnl_net *ip6n, struct ip6_tnl_parm *p) | ip6_tnl_bucket(struct ip6_tnl_net *ip6n, struct ip6_tnl_parm *p) | ||||||
| { | { | ||||||
| 	struct in6_addr *remote = &p->raddr; | 	struct in6_addr *remote = &p->raddr; | ||||||
|  | @ -190,12 +189,10 @@ ip6_tnl_bucket(struct ip6_tnl_net *ip6n, struct ip6_tnl_parm *p) | ||||||
| static void | static void | ||||||
| ip6_tnl_link(struct ip6_tnl_net *ip6n, struct ip6_tnl *t) | ip6_tnl_link(struct ip6_tnl_net *ip6n, struct ip6_tnl *t) | ||||||
| { | { | ||||||
| 	struct ip6_tnl **tp = ip6_tnl_bucket(ip6n, &t->parms); | 	struct ip6_tnl __rcu **tp = ip6_tnl_bucket(ip6n, &t->parms); | ||||||
| 
 | 
 | ||||||
| 	spin_lock_bh(&ip6_tnl_lock); | 	rcu_assign_pointer(t->next , rtnl_dereference(*tp)); | ||||||
| 	t->next = *tp; |  | ||||||
| 	rcu_assign_pointer(*tp, t); | 	rcu_assign_pointer(*tp, t); | ||||||
| 	spin_unlock_bh(&ip6_tnl_lock); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  | @ -206,13 +203,14 @@ ip6_tnl_link(struct ip6_tnl_net *ip6n, struct ip6_tnl *t) | ||||||
| static void | static void | ||||||
| ip6_tnl_unlink(struct ip6_tnl_net *ip6n, struct ip6_tnl *t) | ip6_tnl_unlink(struct ip6_tnl_net *ip6n, struct ip6_tnl *t) | ||||||
| { | { | ||||||
| 	struct ip6_tnl **tp; | 	struct ip6_tnl __rcu **tp; | ||||||
|  | 	struct ip6_tnl *iter; | ||||||
| 
 | 
 | ||||||
| 	for (tp = ip6_tnl_bucket(ip6n, &t->parms); *tp; tp = &(*tp)->next) { | 	for (tp = ip6_tnl_bucket(ip6n, &t->parms); | ||||||
| 		if (t == *tp) { | 	     (iter = rtnl_dereference(*tp)) != NULL; | ||||||
| 			spin_lock_bh(&ip6_tnl_lock); | 	     tp = &iter->next) { | ||||||
| 			*tp = t->next; | 		if (t == iter) { | ||||||
| 			spin_unlock_bh(&ip6_tnl_lock); | 			rcu_assign_pointer(*tp, t->next); | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | @ -290,10 +288,13 @@ static struct ip6_tnl *ip6_tnl_locate(struct net *net, | ||||||
| { | { | ||||||
| 	struct in6_addr *remote = &p->raddr; | 	struct in6_addr *remote = &p->raddr; | ||||||
| 	struct in6_addr *local = &p->laddr; | 	struct in6_addr *local = &p->laddr; | ||||||
|  | 	struct ip6_tnl __rcu **tp; | ||||||
| 	struct ip6_tnl *t; | 	struct ip6_tnl *t; | ||||||
| 	struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); | 	struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); | ||||||
| 
 | 
 | ||||||
| 	for (t = *ip6_tnl_bucket(ip6n, p); t; t = t->next) { | 	for (tp = ip6_tnl_bucket(ip6n, p); | ||||||
|  | 	     (t = rtnl_dereference(*tp)) != NULL; | ||||||
|  | 	     tp = &t->next) { | ||||||
| 		if (ipv6_addr_equal(local, &t->parms.laddr) && | 		if (ipv6_addr_equal(local, &t->parms.laddr) && | ||||||
| 		    ipv6_addr_equal(remote, &t->parms.raddr)) | 		    ipv6_addr_equal(remote, &t->parms.raddr)) | ||||||
| 			return t; | 			return t; | ||||||
|  | @ -318,13 +319,10 @@ ip6_tnl_dev_uninit(struct net_device *dev) | ||||||
| 	struct net *net = dev_net(dev); | 	struct net *net = dev_net(dev); | ||||||
| 	struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); | 	struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); | ||||||
| 
 | 
 | ||||||
| 	if (dev == ip6n->fb_tnl_dev) { | 	if (dev == ip6n->fb_tnl_dev) | ||||||
| 		spin_lock_bh(&ip6_tnl_lock); | 		rcu_assign_pointer(ip6n->tnls_wc[0], NULL); | ||||||
| 		ip6n->tnls_wc[0] = NULL; | 	else | ||||||
| 		spin_unlock_bh(&ip6_tnl_lock); |  | ||||||
| 	} else { |  | ||||||
| 		ip6_tnl_unlink(ip6n, t); | 		ip6_tnl_unlink(ip6n, t); | ||||||
| 	} |  | ||||||
| 	ip6_tnl_dst_reset(t); | 	ip6_tnl_dst_reset(t); | ||||||
| 	dev_put(dev); | 	dev_put(dev); | ||||||
| } | } | ||||||
|  | @ -1369,7 +1367,7 @@ static void __net_init ip6_fb_tnl_dev_init(struct net_device *dev) | ||||||
| 	ip6_tnl_dev_init_gen(dev); | 	ip6_tnl_dev_init_gen(dev); | ||||||
| 	t->parms.proto = IPPROTO_IPV6; | 	t->parms.proto = IPPROTO_IPV6; | ||||||
| 	dev_hold(dev); | 	dev_hold(dev); | ||||||
| 	ip6n->tnls_wc[0] = t; | 	rcu_assign_pointer(ip6n->tnls_wc[0], t); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static struct xfrm6_tunnel ip4ip6_handler __read_mostly = { | 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) | ||||||
| 	LIST_HEAD(list); | 	LIST_HEAD(list); | ||||||
| 
 | 
 | ||||||
| 	for (h = 0; h < HASH_SIZE; h++) { | 	for (h = 0; h < HASH_SIZE; h++) { | ||||||
| 		t = ip6n->tnls_r_l[h]; | 		t = rtnl_dereference(ip6n->tnls_r_l[h]); | ||||||
| 		while (t != NULL) { | 		while (t != NULL) { | ||||||
| 			unregister_netdevice_queue(t->dev, &list); | 			unregister_netdevice_queue(t->dev, &list); | ||||||
| 			t = t->next; | 			t = rtnl_dereference(t->next); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	t = ip6n->tnls_wc[0]; | 	t = rtnl_dereference(ip6n->tnls_wc[0]); | ||||||
| 	unregister_netdevice_queue(t->dev, &list); | 	unregister_netdevice_queue(t->dev, &list); | ||||||
| 	unregister_netdevice_many(&list); | 	unregister_netdevice_many(&list); | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Eric Dumazet
				Eric Dumazet