gre: Network namespace upon device creation.
[openvswitch] / datapath / linux-2.6 / compat-2.6 / ip_gre.c
index 4c47b92530af17f600dca771efce05e4e0dc61ac..2f1a9adbeb78f201dc25d2611fde59e906b97da5 100644 (file)
@@ -178,8 +178,13 @@ struct ipgre_net {
 #define tunnels_r      tunnels[2]
 #define tunnels_l      tunnels[1]
 #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. */
 
@@ -197,7 +202,7 @@ static struct ip_tunnel * ipgre_tunnel_lookup(struct net_device *dev,
                       ARPHRD_ETHER : ARPHRD_IPGRE;
        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 ||
                    remote != t->parms.iph.daddr ||
                    key != t->parms.i_key ||
@@ -222,7 +227,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 ||
                    key != t->parms.i_key ||
                    !(t->dev->flags & IFF_UP))
@@ -246,7 +251,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 &&
                     (local != t->parms.iph.daddr ||
                      !ipv4_is_multicast(local))) ||
@@ -272,7 +277,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 ||
                    !(t->dev->flags & IFF_UP))
                        continue;
@@ -298,8 +303,9 @@ static struct ip_tunnel * ipgre_tunnel_lookup(struct net_device *dev,
        if (cand != NULL)
                return cand;
 
-       if (ign->fb_tunnel_dev->flags & IFF_UP)
-               return netdev_priv(ign->fb_tunnel_dev);
+       dev = ign->fb_tunnel_dev;
+       if (dev->flags & IFF_UP)
+               return netdev_priv(dev);
 
        return NULL;
 }
@@ -333,10 +339,10 @@ static void ipgre_tunnel_link(struct ipgre_net *ign, struct ip_tunnel *t)
 {
        struct ip_tunnel **tp = ipgre_bucket(ign, t);
 
+       spin_lock_bh(&ipgre_lock);
        t->next = *tp;
-       write_lock_bh(&ipgre_lock);
-       *tp = t;
-       write_unlock_bh(&ipgre_lock);
+       rcu_assign_pointer(*tp, t);
+       spin_unlock_bh(&ipgre_lock);
 }
 
 static void ipgre_tunnel_unlink(struct ipgre_net *ign, struct ip_tunnel *t)
@@ -345,9 +351,9 @@ static void ipgre_tunnel_unlink(struct ipgre_net *ign, struct ip_tunnel *t)
 
        for (tp = ipgre_bucket(ign, t); *tp; tp = &(*tp)->next) {
                if (t == *tp) {
-                       write_lock_bh(&ipgre_lock);
+                       spin_lock_bh(&ipgre_lock);
                        *tp = t->next;
-                       write_unlock_bh(&ipgre_lock);
+                       spin_unlock_bh(&ipgre_lock);
                        break;
                }
        }
@@ -504,7 +510,7 @@ static void ipgre_err(struct sk_buff *skb, u32 info)
                break;
        }
 
-       read_lock(&ipgre_lock);
+       rcu_read_lock();
        t = ipgre_tunnel_lookup(skb->dev, iph->daddr, iph->saddr,
                                flags & GRE_KEY ?
                                *(((__be32 *)p) + (grehlen / 4) - 1) : 0,
@@ -522,7 +528,7 @@ static void ipgre_err(struct sk_buff *skb, u32 info)
                t->err_count = 1;
        t->err_time = jiffies;
 out:
-       read_unlock(&ipgre_lock);
+       rcu_read_unlock();
        return;
 }
 
@@ -601,7 +607,7 @@ static int ipgre_rcv(struct sk_buff *skb)
 
        gre_proto = *(__be16 *)(h + 2);
 
-       read_lock(&ipgre_lock);
+       rcu_read_lock();
        if ((tunnel = ipgre_tunnel_lookup(skb->dev,
                                          iph->saddr, iph->daddr, key,
                                          gre_proto))) {
@@ -680,13 +686,13 @@ static int ipgre_rcv(struct sk_buff *skb)
                ipgre_ecn_decapsulate(iph, skb);
 
                netif_rx(skb);
-               read_unlock(&ipgre_lock);
+               rcu_read_unlock();
                return(0);
        }
        icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
 
 drop:
-       read_unlock(&ipgre_lock);
+       rcu_read_unlock();
 drop_nolock:
        kfree_skb(skb);
        return(0);
@@ -1440,16 +1446,19 @@ static const struct net_protocol ipgre_protocol = {
 #endif
 };
 
-static void ipgre_destroy_tunnels(struct ipgre_net *ign)
+static void ipgre_destroy_tunnels(struct ipgre_net *ign, struct list_head *head)
 {
        int prio;
 
        for (prio = 0; prio < 4; prio++) {
                int h;
                for (h = 0; h < HASH_SIZE; h++) {
-                       struct ip_tunnel *t;
-                       while ((t = ign->tunnels[prio][h]) != NULL)
-                               unregister_netdevice(t->dev);
+                       struct ip_tunnel *t = ign->tunnels[prio][h];
+
+                       while (t != NULL) {
+                               unregister_netdevice_queue(t->dev, head);
+                               t = t->next;
+                       }
                }
        }
 }
@@ -1503,10 +1512,12 @@ err_alloc:
 static void ipgre_exit_net(struct net *net)
 {
        struct ipgre_net *ign;
+       LIST_HEAD(list);
 
        ign = net_generic(net, ipgre_net_id);
        rtnl_lock();
-       ipgre_destroy_tunnels(ign);
+       ipgre_destroy_tunnels(ign, &list);
+       unregister_netdevice_many(&list);
        rtnl_unlock();
        kfree(ign);
 }
@@ -1650,8 +1661,13 @@ static void ipgre_netlink_parms(struct nlattr *data[],
                parms->iph.frag_off = htons(IP_DF);
 }
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,33)
+static int ipgre_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[],
+                        struct nlattr *data[])
+#else
 static int ipgre_newlink(struct net_device *dev, struct nlattr *tb[],
                         struct nlattr *data[])
+#endif
 {
        struct ip_tunnel *nt;
        struct net *net = dev_net(dev);