ipv6 sit: RCU conversion phase II
Subsystems (2)
PATCH 2/2 ipv6 sit: RCU conversion phase II
SIT 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 Eric Dumazet <[email protected]>
--- net/ipv6/sit.c | 45 +++++++++++++++++++++++++++------------------ 1 files changed, 27 insertions(+), 18 deletions(-)
net/ipv6/sit.c +27 -18
@@ -77,8 +77,17 @@ struct sit_net {struct net_device *fb_tunnel_dev; }; -static DEFINE_RWLOCK (ipip6_lock); +/* +* Locking : hash tables are protected by RCU and a spinlock +*/ +static DEFINE_SPINLOCK (ipip6_lock); + +#define for_each_ip_tunnel_rcu (start) \ + for (t = rcu_dereference(start); t; t = rcu_dereference(t->next)) +/* +* Must be invoked with rcu_read_lock +*/ static struct ip_tunnel *ipip6_tunnel_lookup (struct net *net,struct net_device *dev,__be32 remote,__be32 local) {
@@ -87,26 +96,26 @@ static struct ip_tunnel * ipip6_tunnel_lookup(struct net *net,struct ip_tunnel *t;struct sit_net *sitn =net_generic (net, sit_net_id); -for (t = sitn->tunnels_r_l[h0^h1]; t; t = t->next) { +for_each_ip_tunnel_rcu (sitn->tunnels_r_l[h0 ^ h1]) {if (local == t->parms.iph.saddr && remote == t->parms.iph.daddr && (!dev || !t->parms.link || dev->iflink == t->parms.link) && (t->dev->flags &IFF_UP ))return t; } -for (t = sitn->tunnels_r[h0]; t; t = t->next) { +for_each_ip_tunnel_rcu (sitn->tunnels_r[h0]) {if (remote == t->parms.iph.daddr && (!dev || !t->parms.link || dev->iflink == t->parms.link) && (t->dev->flags &IFF_UP ))return t; } -for (t = sitn->tunnels_l[h1]; t; t = t->next) { +for_each_ip_tunnel_rcu (sitn->tunnels_l[h1]) {if (local == t->parms.iph.saddr && (!dev || !t->parms.link || dev->iflink == t->parms.link) && (t->dev->flags &IFF_UP ))return t; } - t = sitn->tunnels_wc[0 ]; + t =rcu_dereference (sitn->tunnels_wc[0 ]);if ((t !=NULL ) && (t->dev->flags &IFF_UP ))return t;return NULL ;
@@ -143,9 +152,9 @@ static void ipip6_tunnel_unlink(struct sit_net *sitn, struct ip_tunnel *t)for (tp =ipip6_bucket (sitn, t); *tp; tp = &(*tp)->next) {if (t == *tp) { -write_lock_bh (&ipip6_lock); +spin_lock_bh (&ipip6_lock); *tp = t->next; -write_unlock_bh (&ipip6_lock); +spin_unlock_bh (&ipip6_lock);break ; } }
@@ -155,10 +164,10 @@ static void ipip6_tunnel_link(struct sit_net *sitn, struct ip_tunnel *t) {struct ip_tunnel **tp =ipip6_bucket (sitn, t); +spin_lock_bh (&ipip6_lock); t->next = *tp; -write_lock_bh (&ipip6_lock); - *tp = t; -write_unlock_bh (&ipip6_lock); +rcu_assign_pointer (*tp, t); +spin_unlock_bh (&ipip6_lock); }static void ipip6_tunnel_clone_6rd (struct net_device *dev,struct sit_net *sitn)
@@ -447,9 +456,9 @@ static void ipip6_tunnel_uninit(struct net_device *dev)struct sit_net *sitn =net_generic (net, sit_net_id);if (dev == sitn->fb_tunnel_dev) { -write_lock_bh (&ipip6_lock); +spin_lock_bh (&ipip6_lock); sitn->tunnels_wc[0 ] =NULL ; -write_unlock_bh (&ipip6_lock); +spin_unlock_bh (&ipip6_lock);dev_put (dev); }else {ipip6_tunnel_unlink (sitn,netdev_priv (dev));
@@ -502,7 +511,7 @@ static int ipip6_err(struct sk_buff *skb, u32 info) err = -ENOENT ; -read_lock (&ipip6_lock); +rcu_read_lock (); t = ipip6_tunnel_lookup(dev_net(skb->dev), skb->dev, iph->daddr,
@@ -520,7 +529,7 @@ static int ipip6_err(struct sk_buff *skb, u32 info) t->err_count =1 ; t->err_time = jiffies; out: -read_unlock (&ipip6_lock); +rcu_read_unlock ();return err; }
@@ -540,7 +549,7 @@ static int ipip6_rcv(struct sk_buff *skb) iph =ip_hdr (skb); -read_lock (&ipip6_lock); +rcu_read_lock (); tunnel =ipip6_tunnel_lookup (dev_net (skb->dev), skb->dev, iph->saddr, iph->daddr);if (tunnel !=NULL ) {
@@ -554,7 +563,7 @@ static int ipip6_rcv(struct sk_buff *skb)if ((tunnel->dev->priv_flags &IFF_ISATAP ) && !isatap_chksrc (skb, iph, tunnel)) { tunnel->dev->stats.rx_errors++; -read_unlock (&ipip6_lock); +rcu_read_unlock ();kfree_skb (skb);return 0 ; }
@@ -565,12 +574,12 @@ static int ipip6_rcv(struct sk_buff *skb)nf_reset (skb);ipip6_ecn_decapsulate (iph, skb);netif_rx (skb); -read_unlock (&ipip6_lock); +rcu_read_unlock ();return 0 ; }icmp_send (skb,ICMP_DEST_UNREACH ,ICMP_PORT_UNREACH ,0 ); -read_unlock (&ipip6_lock); +rcu_read_unlock (); out:kfree_skb (skb);return 0 ;