]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - net/ipv4/ip_gre.c
ipip: add module alias for tunl0 tunnel device
[linux-2.6.git] / net / ipv4 / ip_gre.c
index c0c5274d02719831b2b3cd9d15488a1119d8a384..258c98d5fa79fb861fce41e45da19c6201e3a18a 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
+#include <linux/slab.h>
 #include <asm/uaccess.h>
 #include <linux/skbuff.h>
 #include <linux/netdevice.h>
@@ -43,8 +44,9 @@
 #include <net/net_namespace.h>
 #include <net/netns/generic.h>
 #include <net/rtnetlink.h>
+#include <net/gre.h>
 
-#ifdef CONFIG_IPV6
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 #include <net/ipv6.h>
 #include <net/ip6_fib.h>
 #include <net/ip6_route.h>
    We cannot track such dead loops during route installation,
    it is infeasible task. The most general solutions would be
    to keep skb->encapsulation counter (sort of local ttl),
-   and silently drop packet when it expires. It is the best
+   and silently drop packet when it expires. It is a good
    solution, but it supposes maintaing new variable in ALL
    skb, even if no tunneling is used.
 
-   Current solution: HARD_TX_LOCK lock breaks dead loops.
-
-
+   Current solution: xmit_recursion breaks dead loops. This is a percpu
+   counter, since when we enter the first ndo_xmit(), cpu migration is
+   forbidden. We force an exit if this counter reaches RECURSION_LIMIT
 
    2. Networking dead loops would not kill routers, but would really
    kill network. IP hop limit plays role of "t->recursion" in this case,
@@ -127,7 +129,7 @@ static int ipgre_tunnel_bind_dev(struct net_device *dev);
 
 static int ipgre_net_id __read_mostly;
 struct ipgre_net {
-       struct ip_tunnel *tunnels[4][HASH_SIZE];
+       struct ip_tunnel __rcu *tunnels[4][HASH_SIZE];
 
        struct net_device *fb_tunnel_dev;
 };
@@ -157,13 +159,40 @@ struct ipgre_net {
 #define tunnels_l      tunnels[1]
 #define tunnels_wc     tunnels[0]
 /*
- * Locking : hash tables are protected by RCU and a spinlock
+ * Locking : hash tables are protected by RCU and RTNL
  */
-static DEFINE_SPINLOCK(ipgre_lock);
 
 #define for_each_ip_tunnel_rcu(start) \
        for (t = rcu_dereference(start); t; t = rcu_dereference(t->next))
 
+/* often modified stats are per cpu, other are shared (netdev->stats) */
+struct pcpu_tstats {
+       unsigned long   rx_packets;
+       unsigned long   rx_bytes;
+       unsigned long   tx_packets;
+       unsigned long   tx_bytes;
+};
+
+static struct net_device_stats *ipgre_get_stats(struct net_device *dev)
+{
+       struct pcpu_tstats sum = { 0 };
+       int i;
+
+       for_each_possible_cpu(i) {
+               const struct pcpu_tstats *tstats = per_cpu_ptr(dev->tstats, i);
+
+               sum.rx_packets += tstats->rx_packets;
+               sum.rx_bytes   += tstats->rx_bytes;
+               sum.tx_packets += tstats->tx_packets;
+               sum.tx_bytes   += tstats->tx_bytes;
+       }
+       dev->stats.rx_packets = sum.rx_packets;
+       dev->stats.rx_bytes   = sum.rx_bytes;
+       dev->stats.tx_packets = sum.tx_packets;
+       dev->stats.tx_bytes   = sum.tx_bytes;
+       return &dev->stats;
+}
+
 /* Given src, dst and key, find appropriate for input tunnel. */
 
 static struct ip_tunnel * ipgre_tunnel_lookup(struct net_device *dev,
@@ -172,8 +201,8 @@ static struct ip_tunnel * ipgre_tunnel_lookup(struct net_device *dev,
 {
        struct net *net = dev_net(dev);
        int link = dev->ifindex;
-       unsigned h0 = HASH(remote);
-       unsigned h1 = HASH(key);
+       unsigned int h0 = HASH(remote);
+       unsigned int h1 = HASH(key);
        struct ip_tunnel *t, *cand = NULL;
        struct ipgre_net *ign = net_generic(net, ipgre_net_id);
        int dev_type = (gre_proto == htons(ETH_P_TEB)) ?
@@ -288,13 +317,13 @@ static struct ip_tunnel * ipgre_tunnel_lookup(struct net_device *dev,
        return NULL;
 }
 
-static struct ip_tunnel **__ipgre_bucket(struct ipgre_net *ign,
+static struct ip_tunnel __rcu **__ipgre_bucket(struct ipgre_net *ign,
                struct ip_tunnel_parm *parms)
 {
        __be32 remote = parms->iph.daddr;
        __be32 local = parms->iph.saddr;
        __be32 key = parms->i_key;
-       unsigned h = HASH(key);
+       unsigned int h = HASH(key);
        int prio = 0;
 
        if (local)
@@ -307,7 +336,7 @@ static struct ip_tunnel **__ipgre_bucket(struct ipgre_net *ign,
        return &ign->tunnels[prio][h];
 }
 
-static inline struct ip_tunnel **ipgre_bucket(struct ipgre_net *ign,
+static inline struct ip_tunnel __rcu **ipgre_bucket(struct ipgre_net *ign,
                struct ip_tunnel *t)
 {
        return __ipgre_bucket(ign, &t->parms);
@@ -315,23 +344,22 @@ static inline struct ip_tunnel **ipgre_bucket(struct ipgre_net *ign,
 
 static void ipgre_tunnel_link(struct ipgre_net *ign, struct ip_tunnel *t)
 {
-       struct ip_tunnel **tp = ipgre_bucket(ign, t);
+       struct ip_tunnel __rcu **tp = ipgre_bucket(ign, t);
 
-       spin_lock_bh(&ipgre_lock);
-       t->next = *tp;
+       rcu_assign_pointer(t->next, rtnl_dereference(*tp));
        rcu_assign_pointer(*tp, t);
-       spin_unlock_bh(&ipgre_lock);
 }
 
 static void ipgre_tunnel_unlink(struct ipgre_net *ign, struct ip_tunnel *t)
 {
-       struct ip_tunnel **tp;
-
-       for (tp = ipgre_bucket(ign, t); *tp; tp = &(*tp)->next) {
-               if (t == *tp) {
-                       spin_lock_bh(&ipgre_lock);
-                       *tp = t->next;
-                       spin_unlock_bh(&ipgre_lock);
+       struct ip_tunnel __rcu **tp;
+       struct ip_tunnel *iter;
+
+       for (tp = ipgre_bucket(ign, t);
+            (iter = rtnl_dereference(*tp)) != NULL;
+            tp = &iter->next) {
+               if (t == iter) {
+                       rcu_assign_pointer(*tp, t->next);
                        break;
                }
        }
@@ -345,10 +373,13 @@ static struct ip_tunnel *ipgre_tunnel_find(struct net *net,
        __be32 local = parms->iph.saddr;
        __be32 key = parms->i_key;
        int link = parms->link;
-       struct ip_tunnel *t, **tp;
+       struct ip_tunnel *t;
+       struct ip_tunnel __rcu **tp;
        struct ipgre_net *ign = net_generic(net, ipgre_net_id);
 
-       for (tp = __ipgre_bucket(ign, parms); (t = *tp) != NULL; tp = &t->next)
+       for (tp = __ipgre_bucket(ign, parms);
+            (t = rtnl_dereference(*tp)) != NULL;
+            tp = &t->next)
                if (local == t->parms.iph.saddr &&
                    remote == t->parms.iph.daddr &&
                    key == t->parms.i_key &&
@@ -359,7 +390,7 @@ static struct ip_tunnel *ipgre_tunnel_find(struct net *net,
        return t;
 }
 
-static struct ip_tunnel * ipgre_tunnel_locate(struct net *net,
+static struct ip_tunnel *ipgre_tunnel_locate(struct net *net,
                struct ip_tunnel_parm *parms, int create)
 {
        struct ip_tunnel *t, *nt;
@@ -374,11 +405,11 @@ static struct ip_tunnel * ipgre_tunnel_locate(struct net *net,
        if (parms->name[0])
                strlcpy(name, parms->name, IFNAMSIZ);
        else
-               sprintf(name, "gre%%d");
+               strcpy(name, "gre%d");
 
        dev = alloc_netdev(sizeof(*t), name, ipgre_tunnel_setup);
        if (!dev)
-         return NULL;
+               return NULL;
 
        dev_net_set(dev, net);
 
@@ -501,7 +532,6 @@ static void ipgre_err(struct sk_buff *skb, u32 info)
        t->err_time = jiffies;
 out:
        rcu_read_unlock();
-       return;
 }
 
 static inline void ipgre_ecn_decapsulate(struct iphdr *iph, struct sk_buff *skb)
@@ -537,7 +567,6 @@ static int ipgre_rcv(struct sk_buff *skb)
        struct ip_tunnel *tunnel;
        int    offset = 4;
        __be16 gre_proto;
-       unsigned int len;
 
        if (!pskb_may_pull(skb, 16))
                goto drop_nolock;
@@ -583,7 +612,7 @@ static int ipgre_rcv(struct sk_buff *skb)
        if ((tunnel = ipgre_tunnel_lookup(skb->dev,
                                          iph->saddr, iph->daddr, key,
                                          gre_proto))) {
-               struct net_device_stats *stats = &tunnel->dev->stats;
+               struct pcpu_tstats *tstats;
 
                secpath_reset(skb);
 
@@ -605,36 +634,34 @@ static int ipgre_rcv(struct sk_buff *skb)
 #ifdef CONFIG_NET_IPGRE_BROADCAST
                if (ipv4_is_multicast(iph->daddr)) {
                        /* Looped back packet, drop it! */
-                       if (skb_rtable(skb)->fl.iif == 0)
+                       if (rt_is_output_route(skb_rtable(skb)))
                                goto drop;
-                       stats->multicast++;
+                       tunnel->dev->stats.multicast++;
                        skb->pkt_type = PACKET_BROADCAST;
                }
 #endif
 
                if (((flags&GRE_CSUM) && csum) ||
                    (!(flags&GRE_CSUM) && tunnel->parms.i_flags&GRE_CSUM)) {
-                       stats->rx_crc_errors++;
-                       stats->rx_errors++;
+                       tunnel->dev->stats.rx_crc_errors++;
+                       tunnel->dev->stats.rx_errors++;
                        goto drop;
                }
                if (tunnel->parms.i_flags&GRE_SEQ) {
                        if (!(flags&GRE_SEQ) ||
                            (tunnel->i_seqno && (s32)(seqno - tunnel->i_seqno) < 0)) {
-                               stats->rx_fifo_errors++;
-                               stats->rx_errors++;
+                               tunnel->dev->stats.rx_fifo_errors++;
+                               tunnel->dev->stats.rx_errors++;
                                goto drop;
                        }
                        tunnel->i_seqno = seqno + 1;
                }
 
-               len = skb->len;
-
                /* Warning: All skb pointers will be invalidated! */
                if (tunnel->dev->type == ARPHRD_ETHER) {
                        if (!pskb_may_pull(skb, ETH_HLEN)) {
-                               stats->rx_length_errors++;
-                               stats->rx_errors++;
+                               tunnel->dev->stats.rx_length_errors++;
+                               tunnel->dev->stats.rx_errors++;
                                goto drop;
                        }
 
@@ -643,18 +670,19 @@ static int ipgre_rcv(struct sk_buff *skb)
                        skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN);
                }
 
-               stats->rx_packets++;
-               stats->rx_bytes += len;
-               skb->dev = tunnel->dev;
-               skb_dst_drop(skb);
-               nf_reset(skb);
+               tstats = this_cpu_ptr(tunnel->dev->tstats);
+               tstats->rx_packets++;
+               tstats->rx_bytes += skb->len;
+
+               __skb_tunnel_rx(skb, tunnel->dev);
 
                skb_reset_network_header(skb);
                ipgre_ecn_decapsulate(iph, skb);
 
                netif_rx(skb);
+
                rcu_read_unlock();
-               return(0);
+               return 0;
        }
        icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
 
@@ -662,20 +690,19 @@ drop:
        rcu_read_unlock();
 drop_nolock:
        kfree_skb(skb);
-       return(0);
+       return 0;
 }
 
 static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct ip_tunnel *tunnel = netdev_priv(dev);
-       struct net_device_stats *stats = &dev->stats;
-       struct netdev_queue *txq = netdev_get_tx_queue(dev, 0);
+       struct pcpu_tstats *tstats;
        struct iphdr  *old_iph = ip_hdr(skb);
        struct iphdr  *tiph;
        u8     tos;
        __be16 df;
        struct rtable *rt;                      /* Route to the other host */
-       struct net_device *tdev;                        /* Device to other host */
+       struct net_device *tdev;                /* Device to other host */
        struct iphdr  *iph;                     /* Our new IP header */
        unsigned int max_headroom;              /* The extra header space needed */
        int    gre_hlen;
@@ -697,7 +724,7 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
                /* NBMA tunnel */
 
                if (skb_dst(skb) == NULL) {
-                       stats->tx_fifo_errors++;
+                       dev->stats.tx_fifo_errors++;
                        goto tx_error;
                }
 
@@ -706,7 +733,7 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
                        if ((dst = rt->rt_gateway) == 0)
                                goto tx_error_icmp;
                }
-#ifdef CONFIG_IPV6
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
                else if (skb->protocol == htons(ETH_P_IPV6)) {
                        struct in6_addr *addr6;
                        int addr_type;
@@ -738,31 +765,34 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
                tos = 0;
                if (skb->protocol == htons(ETH_P_IP))
                        tos = old_iph->tos;
+               else if (skb->protocol == htons(ETH_P_IPV6))
+                       tos = ipv6_get_dsfield((struct ipv6hdr *)old_iph);
        }
 
        {
-               struct flowi fl = { .oif = tunnel->parms.link,
-                                   .nl_u = { .ip4_u =
-                                             { .daddr = dst,
-                                               .saddr = tiph->saddr,
-                                               .tos = RT_TOS(tos) } },
-                                   .proto = IPPROTO_GRE };
+               struct flowi fl = {
+                       .oif = tunnel->parms.link,
+                       .fl4_dst = dst,
+                       .fl4_src = tiph->saddr,
+                       .fl4_tos = RT_TOS(tos),
+                       .fl_gre_key = tunnel->parms.o_key
+               };
                if (ip_route_output_key(dev_net(dev), &rt, &fl)) {
-                       stats->tx_carrier_errors++;
+                       dev->stats.tx_carrier_errors++;
                        goto tx_error;
                }
        }
-       tdev = rt->u.dst.dev;
+       tdev = rt->dst.dev;
 
        if (tdev == dev) {
                ip_rt_put(rt);
-               stats->collisions++;
+               dev->stats.collisions++;
                goto tx_error;
        }
 
        df = tiph->frag_off;
        if (df)
-               mtu = dst_mtu(&rt->u.dst) - dev->hard_header_len - tunnel->hlen;
+               mtu = dst_mtu(&rt->dst) - dev->hard_header_len - tunnel->hlen;
        else
                mtu = skb_dst(skb) ? dst_mtu(skb_dst(skb)) : dev->mtu;
 
@@ -779,7 +809,7 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
                        goto tx_error;
                }
        }
-#ifdef CONFIG_IPV6
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
        else if (skb->protocol == htons(ETH_P_IPV6)) {
                struct rt6_info *rt6 = (struct rt6_info *)skb_dst(skb);
 
@@ -810,14 +840,16 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
                        tunnel->err_count = 0;
        }
 
-       max_headroom = LL_RESERVED_SPACE(tdev) + gre_hlen;
+       max_headroom = LL_RESERVED_SPACE(tdev) + gre_hlen + rt->dst.header_len;
 
        if (skb_headroom(skb) < max_headroom || skb_shared(skb)||
            (skb_cloned(skb) && !skb_clone_writable(skb, 0))) {
                struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom);
+               if (max_headroom > dev->needed_headroom)
+                       dev->needed_headroom = max_headroom;
                if (!new_skb) {
                        ip_rt_put(rt);
-                       txq->tx_dropped++;
+                       dev->stats.tx_dropped++;
                        dev_kfree_skb(skb);
                        return NETDEV_TX_OK;
                }
@@ -835,7 +867,7 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
        IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
                              IPSKB_REROUTED);
        skb_dst_drop(skb);
-       skb_dst_set(skb, &rt->u.dst);
+       skb_dst_set(skb, &rt->dst);
 
        /*
         *      Push down and install the IPIP header.
@@ -853,12 +885,12 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
        if ((iph->ttl = tiph->ttl) == 0) {
                if (skb->protocol == htons(ETH_P_IP))
                        iph->ttl = old_iph->ttl;
-#ifdef CONFIG_IPV6
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
                else if (skb->protocol == htons(ETH_P_IPV6))
                        iph->ttl = ((struct ipv6hdr *)old_iph)->hop_limit;
 #endif
                else
-                       iph->ttl = dst_metric(&rt->u.dst, RTAX_HOPLIMIT);
+                       iph->ttl = dst_metric(&rt->dst, RTAX_HOPLIMIT);
        }
 
        ((__be16 *)(iph + 1))[0] = tunnel->parms.o_flags;
@@ -884,15 +916,15 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
        }
 
        nf_reset(skb);
-
-       IPTUNNEL_XMIT();
+       tstats = this_cpu_ptr(dev->tstats);
+       __IPTUNNEL_XMIT(tstats, &dev->stats);
        return NETDEV_TX_OK;
 
 tx_error_icmp:
        dst_link_failure(skb);
 
 tx_error:
-       stats->tx_errors++;
+       dev->stats.tx_errors++;
        dev_kfree_skb(skb);
        return NETDEV_TX_OK;
 }
@@ -912,15 +944,18 @@ static int ipgre_tunnel_bind_dev(struct net_device *dev)
        /* Guess output device to choose reasonable mtu and needed_headroom */
 
        if (iph->daddr) {
-               struct flowi fl = { .oif = tunnel->parms.link,
-                                   .nl_u = { .ip4_u =
-                                             { .daddr = iph->daddr,
-                                               .saddr = iph->saddr,
-                                               .tos = RT_TOS(iph->tos) } },
-                                   .proto = IPPROTO_GRE };
+               struct flowi fl = {
+                       .oif = tunnel->parms.link,
+                       .fl4_dst = iph->daddr,
+                       .fl4_src = iph->saddr,
+                       .fl4_tos = RT_TOS(iph->tos),
+                       .proto = IPPROTO_GRE,
+                       .fl_gre_key = tunnel->parms.o_key
+               };
                struct rtable *rt;
+
                if (!ip_route_output_key(dev_net(dev), &rt, &fl)) {
-                       tdev = rt->u.dst.dev;
+                       tdev = rt->dst.dev;
                        ip_rt_put(rt);
                }
 
@@ -1015,7 +1050,7 @@ ipgre_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
                                        break;
                                }
                        } else {
-                               unsigned nflags = 0;
+                               unsigned int nflags = 0;
 
                                t = netdev_priv(dev);
 
@@ -1029,6 +1064,7 @@ ipgre_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
                                        break;
                                }
                                ipgre_tunnel_unlink(ign, t);
+                               synchronize_net();
                                t->parms.iph.saddr = p.iph.saddr;
                                t->parms.iph.daddr = p.iph.daddr;
                                t->parms.i_key = p.i_key;
@@ -1128,7 +1164,7 @@ static int ipgre_tunnel_change_mtu(struct net_device *dev, int new_mtu)
 
 static int ipgre_header(struct sk_buff *skb, struct net_device *dev,
                        unsigned short type,
-                       const void *daddr, const void *saddr, unsigned len)
+                       const void *daddr, const void *saddr, unsigned int len)
 {
        struct ip_tunnel *t = netdev_priv(dev);
        struct iphdr *iph = (struct iphdr *)skb_push(skb, t->hlen);
@@ -1144,12 +1180,9 @@ static int ipgre_header(struct sk_buff *skb, struct net_device *dev,
 
        if (saddr)
                memcpy(&iph->saddr, saddr, 4);
-
-       if (daddr) {
+       if (daddr)
                memcpy(&iph->daddr, daddr, 4);
-               return t->hlen;
-       }
-       if (iph->daddr && !ipv4_is_multicast(iph->daddr))
+       if (iph->daddr)
                return t->hlen;
 
        return -t->hlen;
@@ -1173,16 +1206,19 @@ static int ipgre_open(struct net_device *dev)
        struct ip_tunnel *t = netdev_priv(dev);
 
        if (ipv4_is_multicast(t->parms.iph.daddr)) {
-               struct flowi fl = { .oif = t->parms.link,
-                                   .nl_u = { .ip4_u =
-                                             { .daddr = t->parms.iph.daddr,
-                                               .saddr = t->parms.iph.saddr,
-                                               .tos = RT_TOS(t->parms.iph.tos) } },
-                                   .proto = IPPROTO_GRE };
+               struct flowi fl = {
+                       .oif = t->parms.link,
+                       .fl4_dst = t->parms.iph.daddr,
+                       .fl4_src = t->parms.iph.saddr,
+                       .fl4_tos = RT_TOS(t->parms.iph.tos),
+                       .proto = IPPROTO_GRE,
+                       .fl_gre_key = t->parms.o_key
+               };
                struct rtable *rt;
+
                if (ip_route_output_key(dev_net(dev), &rt, &fl))
                        return -EADDRNOTAVAIL;
-               dev = rt->u.dst.dev;
+               dev = rt->dst.dev;
                ip_rt_put(rt);
                if (__in_dev_get_rtnl(dev) == NULL)
                        return -EADDRNOTAVAIL;
@@ -1199,10 +1235,8 @@ static int ipgre_close(struct net_device *dev)
        if (ipv4_is_multicast(t->parms.iph.daddr) && t->mlink) {
                struct in_device *in_dev;
                in_dev = inetdev_by_index(dev_net(dev), t->mlink);
-               if (in_dev) {
+               if (in_dev)
                        ip_mc_dec_group(in_dev, t->parms.iph.daddr);
-                       in_dev_put(in_dev);
-               }
        }
        return 0;
 }
@@ -1219,12 +1253,19 @@ static const struct net_device_ops ipgre_netdev_ops = {
        .ndo_start_xmit         = ipgre_tunnel_xmit,
        .ndo_do_ioctl           = ipgre_tunnel_ioctl,
        .ndo_change_mtu         = ipgre_tunnel_change_mtu,
+       .ndo_get_stats          = ipgre_get_stats,
 };
 
+static void ipgre_dev_free(struct net_device *dev)
+{
+       free_percpu(dev->tstats);
+       free_netdev(dev);
+}
+
 static void ipgre_tunnel_setup(struct net_device *dev)
 {
        dev->netdev_ops         = &ipgre_netdev_ops;
-       dev->destructor         = free_netdev;
+       dev->destructor         = ipgre_dev_free;
 
        dev->type               = ARPHRD_IPGRE;
        dev->needed_headroom    = LL_MAX_HEADER + sizeof(struct iphdr) + 4;
@@ -1262,6 +1303,10 @@ static int ipgre_tunnel_init(struct net_device *dev)
        } else
                dev->header_ops = &ipgre_header_ops;
 
+       dev->tstats = alloc_percpu(struct pcpu_tstats);
+       if (!dev->tstats)
+               return -ENOMEM;
+
        return 0;
 }
 
@@ -1269,7 +1314,6 @@ static void ipgre_fb_tunnel_init(struct net_device *dev)
 {
        struct ip_tunnel *tunnel = netdev_priv(dev);
        struct iphdr *iph = &tunnel->parms.iph;
-       struct ipgre_net *ign = net_generic(dev_net(dev), ipgre_net_id);
 
        tunnel->dev = dev;
        strcpy(tunnel->parms.name, dev->name);
@@ -1280,14 +1324,12 @@ static void ipgre_fb_tunnel_init(struct net_device *dev)
        tunnel->hlen            = sizeof(struct iphdr) + 4;
 
        dev_hold(dev);
-       ign->tunnels_wc[0]      = tunnel;
 }
 
 
-static const struct net_protocol ipgre_protocol = {
-       .handler        =       ipgre_rcv,
-       .err_handler    =       ipgre_err,
-       .netns_ok       =       1,
+static const struct gre_protocol ipgre_protocol = {
+       .handler     = ipgre_rcv,
+       .err_handler = ipgre_err,
 };
 
 static void ipgre_destroy_tunnels(struct ipgre_net *ign, struct list_head *head)
@@ -1297,11 +1339,13 @@ static void ipgre_destroy_tunnels(struct ipgre_net *ign, struct list_head *head)
        for (prio = 0; prio < 4; prio++) {
                int h;
                for (h = 0; h < HASH_SIZE; h++) {
-                       struct ip_tunnel *t = ign->tunnels[prio][h];
+                       struct ip_tunnel *t;
+
+                       t = rtnl_dereference(ign->tunnels[prio][h]);
 
                        while (t != NULL) {
                                unregister_netdevice_queue(t->dev, head);
-                               t = t->next;
+                               t = rtnl_dereference(t->next);
                        }
                }
        }
@@ -1326,10 +1370,12 @@ static int __net_init ipgre_init_net(struct net *net)
        if ((err = register_netdev(ign->fb_tunnel_dev)))
                goto err_reg_dev;
 
+       rcu_assign_pointer(ign->tunnels_wc[0],
+                          netdev_priv(ign->fb_tunnel_dev));
        return 0;
 
 err_reg_dev:
-       free_netdev(ign->fb_tunnel_dev);
+       ipgre_dev_free(ign->fb_tunnel_dev);
 err_alloc_dev:
        return err;
 }
@@ -1447,6 +1493,10 @@ static int ipgre_tap_init(struct net_device *dev)
 
        ipgre_tunnel_bind_dev(dev);
 
+       dev->tstats = alloc_percpu(struct pcpu_tstats);
+       if (!dev->tstats)
+               return -ENOMEM;
+
        return 0;
 }
 
@@ -1457,6 +1507,7 @@ static const struct net_device_ops ipgre_tap_netdev_ops = {
        .ndo_set_mac_address    = eth_mac_addr,
        .ndo_validate_addr      = eth_validate_addr,
        .ndo_change_mtu         = ipgre_tunnel_change_mtu,
+       .ndo_get_stats          = ipgre_get_stats,
 };
 
 static void ipgre_tap_setup(struct net_device *dev)
@@ -1465,7 +1516,7 @@ static void ipgre_tap_setup(struct net_device *dev)
        ether_setup(dev);
 
        dev->netdev_ops         = &ipgre_tap_netdev_ops;
-       dev->destructor         = free_netdev;
+       dev->destructor         = ipgre_dev_free;
 
        dev->iflink             = 0;
        dev->features           |= NETIF_F_NETNS_LOCAL;
@@ -1493,6 +1544,10 @@ static int ipgre_newlink(struct net *src_net, struct net_device *dev, struct nla
        if (!tb[IFLA_MTU])
                dev->mtu = mtu;
 
+       /* Can use a lockless transmit, unless we generate output sequences */
+       if (!(nt->parms.o_flags & GRE_SEQ))
+               dev->features |= NETIF_F_LLTX;
+
        err = register_netdevice(dev);
        if (err)
                goto out;
@@ -1528,7 +1583,7 @@ static int ipgre_changelink(struct net_device *dev, struct nlattr *tb[],
                t = nt;
 
                if (dev->type != ARPHRD_ETHER) {
-                       unsigned nflags = 0;
+                       unsigned int nflags = 0;
 
                        if (ipv4_is_multicast(p.iph.daddr))
                                nflags = IFF_BROADCAST;
@@ -1669,7 +1724,7 @@ static int __init ipgre_init(void)
        if (err < 0)
                return err;
 
-       err = inet_add_protocol(&ipgre_protocol, IPPROTO_GRE);
+       err = gre_add_protocol(&ipgre_protocol, GREPROTO_CISCO);
        if (err < 0) {
                printk(KERN_INFO "ipgre init: can't add protocol\n");
                goto add_proto_failed;
@@ -1689,7 +1744,7 @@ out:
 tap_ops_failed:
        rtnl_link_unregister(&ipgre_link_ops);
 rtnl_link_failed:
-       inet_del_protocol(&ipgre_protocol, IPPROTO_GRE);
+       gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO);
 add_proto_failed:
        unregister_pernet_device(&ipgre_net_ops);
        goto out;
@@ -1699,7 +1754,7 @@ static void __exit ipgre_fini(void)
 {
        rtnl_link_unregister(&ipgre_tap_ops);
        rtnl_link_unregister(&ipgre_link_ops);
-       if (inet_del_protocol(&ipgre_protocol, IPPROTO_GRE) < 0)
+       if (gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO) < 0)
                printk(KERN_INFO "ipgre close: can't remove protocol\n");
        unregister_pernet_device(&ipgre_net_ops);
 }
@@ -1709,3 +1764,4 @@ module_exit(ipgre_fini);
 MODULE_LICENSE("GPL");
 MODULE_ALIAS_RTNL_LINK("gre");
 MODULE_ALIAS_RTNL_LINK("gretap");
+MODULE_ALIAS("gre0");