Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8d5b2c08 authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller
Browse files

gre: convert hash tables locking to RCU



GRE tunnels use one rwlock to protect their hash tables.

This locking scheme can be converted to RCU for free, since netdevice
already must wait for a RCU grace period at dismantle time.

Signed-off-by: default avatarEric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 2922bc8a
Loading
Loading
Loading
Loading
+23 −17
Original line number Original line Diff line number Diff line
@@ -156,8 +156,13 @@ struct ipgre_net {
#define tunnels_r	tunnels[2]
#define tunnels_r	tunnels[2]
#define tunnels_l	tunnels[1]
#define tunnels_l	tunnels[1]
#define tunnels_wc	tunnels[0]
#define tunnels_wc	tunnels[0]
/*
 * Locking : hash tables are protected by RCU and a spinlock
 */
static DEFINE_SPINLOCK(ipgre_lock);


static DEFINE_RWLOCK(ipgre_lock);
#define for_each_ip_tunnel_rcu(start) \
	for (t = rcu_dereference(start); t; t = rcu_dereference(t->next))


/* Given src, dst and key, find appropriate for input tunnel. */
/* Given src, dst and key, find appropriate for input tunnel. */


@@ -175,7 +180,7 @@ static struct ip_tunnel * ipgre_tunnel_lookup(struct net_device *dev,
		       ARPHRD_ETHER : ARPHRD_IPGRE;
		       ARPHRD_ETHER : ARPHRD_IPGRE;
	int score, cand_score = 4;
	int score, cand_score = 4;


	for (t = ign->tunnels_r_l[h0^h1]; t; t = t->next) {
	for_each_ip_tunnel_rcu(ign->tunnels_r_l[h0 ^ h1]) {
		if (local != t->parms.iph.saddr ||
		if (local != t->parms.iph.saddr ||
		    remote != t->parms.iph.daddr ||
		    remote != t->parms.iph.daddr ||
		    key != t->parms.i_key ||
		    key != t->parms.i_key ||
@@ -200,7 +205,7 @@ static struct ip_tunnel * ipgre_tunnel_lookup(struct net_device *dev,
		}
		}
	}
	}


	for (t = ign->tunnels_r[h0^h1]; t; t = t->next) {
	for_each_ip_tunnel_rcu(ign->tunnels_r[h0 ^ h1]) {
		if (remote != t->parms.iph.daddr ||
		if (remote != t->parms.iph.daddr ||
		    key != t->parms.i_key ||
		    key != t->parms.i_key ||
		    !(t->dev->flags & IFF_UP))
		    !(t->dev->flags & IFF_UP))
@@ -224,7 +229,7 @@ static struct ip_tunnel * ipgre_tunnel_lookup(struct net_device *dev,
		}
		}
	}
	}


	for (t = ign->tunnels_l[h1]; t; t = t->next) {
	for_each_ip_tunnel_rcu(ign->tunnels_l[h1]) {
		if ((local != t->parms.iph.saddr &&
		if ((local != t->parms.iph.saddr &&
		     (local != t->parms.iph.daddr ||
		     (local != t->parms.iph.daddr ||
		      !ipv4_is_multicast(local))) ||
		      !ipv4_is_multicast(local))) ||
@@ -250,7 +255,7 @@ static struct ip_tunnel * ipgre_tunnel_lookup(struct net_device *dev,
		}
		}
	}
	}


	for (t = ign->tunnels_wc[h1]; t; t = t->next) {
	for_each_ip_tunnel_rcu(ign->tunnels_wc[h1]) {
		if (t->parms.i_key != key ||
		if (t->parms.i_key != key ||
		    !(t->dev->flags & IFF_UP))
		    !(t->dev->flags & IFF_UP))
			continue;
			continue;
@@ -276,8 +281,9 @@ static struct ip_tunnel * ipgre_tunnel_lookup(struct net_device *dev,
	if (cand != NULL)
	if (cand != NULL)
		return cand;
		return cand;


	if (ign->fb_tunnel_dev->flags & IFF_UP)
	dev = ign->fb_tunnel_dev;
		return netdev_priv(ign->fb_tunnel_dev);
	if (dev->flags & IFF_UP)
		return netdev_priv(dev);


	return NULL;
	return NULL;
}
}
@@ -311,10 +317,10 @@ static void ipgre_tunnel_link(struct ipgre_net *ign, struct ip_tunnel *t)
{
{
	struct ip_tunnel **tp = ipgre_bucket(ign, t);
	struct ip_tunnel **tp = ipgre_bucket(ign, t);


	spin_lock_bh(&ipgre_lock);
	t->next = *tp;
	t->next = *tp;
	write_lock_bh(&ipgre_lock);
	rcu_assign_pointer(*tp, t);
	*tp = t;
	spin_unlock_bh(&ipgre_lock);
	write_unlock_bh(&ipgre_lock);
}
}


static void ipgre_tunnel_unlink(struct ipgre_net *ign, struct ip_tunnel *t)
static void ipgre_tunnel_unlink(struct ipgre_net *ign, struct ip_tunnel *t)
@@ -323,9 +329,9 @@ static void ipgre_tunnel_unlink(struct ipgre_net *ign, struct ip_tunnel *t)


	for (tp = ipgre_bucket(ign, t); *tp; tp = &(*tp)->next) {
	for (tp = ipgre_bucket(ign, t); *tp; tp = &(*tp)->next) {
		if (t == *tp) {
		if (t == *tp) {
			write_lock_bh(&ipgre_lock);
			spin_lock_bh(&ipgre_lock);
			*tp = t->next;
			*tp = t->next;
			write_unlock_bh(&ipgre_lock);
			spin_unlock_bh(&ipgre_lock);
			break;
			break;
		}
		}
	}
	}
@@ -476,7 +482,7 @@ static void ipgre_err(struct sk_buff *skb, u32 info)
		break;
		break;
	}
	}


	read_lock(&ipgre_lock);
	rcu_read_lock();
	t = ipgre_tunnel_lookup(skb->dev, iph->daddr, iph->saddr,
	t = ipgre_tunnel_lookup(skb->dev, iph->daddr, iph->saddr,
				flags & GRE_KEY ?
				flags & GRE_KEY ?
				*(((__be32 *)p) + (grehlen / 4) - 1) : 0,
				*(((__be32 *)p) + (grehlen / 4) - 1) : 0,
@@ -494,7 +500,7 @@ static void ipgre_err(struct sk_buff *skb, u32 info)
		t->err_count = 1;
		t->err_count = 1;
	t->err_time = jiffies;
	t->err_time = jiffies;
out:
out:
	read_unlock(&ipgre_lock);
	rcu_read_unlock();
	return;
	return;
}
}


@@ -573,7 +579,7 @@ static int ipgre_rcv(struct sk_buff *skb)


	gre_proto = *(__be16 *)(h + 2);
	gre_proto = *(__be16 *)(h + 2);


	read_lock(&ipgre_lock);
	rcu_read_lock();
	if ((tunnel = ipgre_tunnel_lookup(skb->dev,
	if ((tunnel = ipgre_tunnel_lookup(skb->dev,
					  iph->saddr, iph->daddr, key,
					  iph->saddr, iph->daddr, key,
					  gre_proto))) {
					  gre_proto))) {
@@ -647,13 +653,13 @@ static int ipgre_rcv(struct sk_buff *skb)
		ipgre_ecn_decapsulate(iph, skb);
		ipgre_ecn_decapsulate(iph, skb);


		netif_rx(skb);
		netif_rx(skb);
		read_unlock(&ipgre_lock);
		rcu_read_unlock();
		return(0);
		return(0);
	}
	}
	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);


drop:
drop:
	read_unlock(&ipgre_lock);
	rcu_read_unlock();
drop_nolock:
drop_nolock:
	kfree_skb(skb);
	kfree_skb(skb);
	return(0);
	return(0);