struct net *net = tunnel->net;
        struct net_device *tdev;    /* Device to other host */
        struct ipv6hdr  *ipv6h;     /* Our new IP header */
-       unsigned int max_headroom = 0; /* The extra header space needed */
+       unsigned int min_headroom = 0; /* The extra header space needed */
        int    gre_hlen;
        struct ipv6_tel_txoption opt;
        int    mtu;
        struct net_device_stats *stats = &tunnel->dev->stats;
        int err = -1;
        u8 proto;
-       struct sk_buff *new_skb;
        __be16 protocol;
 
        if (dev->type == ARPHRD_ETHER)
 
        mtu = dst_mtu(dst) - sizeof(*ipv6h);
        if (encap_limit >= 0) {
-               max_headroom += 8;
+               min_headroom += 8;
                mtu -= 8;
        }
        if (mtu < IPV6_MIN_MTU)
                mtu = IPV6_MIN_MTU;
        if (skb_dst(skb))
                skb_dst(skb)->ops->update_pmtu(skb_dst(skb), NULL, skb, mtu);
-       if (skb->len > mtu) {
+       if (skb->len > mtu && !skb_is_gso(skb)) {
                *pmtu = mtu;
                err = -EMSGSIZE;
                goto tx_err_dst_release;
 
        skb_scrub_packet(skb, !net_eq(tunnel->net, dev_net(dev)));
 
-       max_headroom += LL_RESERVED_SPACE(tdev) + gre_hlen + dst->header_len;
+       min_headroom += LL_RESERVED_SPACE(tdev) + gre_hlen + dst->header_len;
 
-       if (skb_headroom(skb) < max_headroom || skb_shared(skb) ||
-           (skb_cloned(skb) && !skb_clone_writable(skb, 0))) {
-               new_skb = skb_realloc_headroom(skb, max_headroom);
-               if (max_headroom > dev->needed_headroom)
-                       dev->needed_headroom = max_headroom;
-               if (!new_skb)
-                       goto tx_err_dst_release;
+       if (skb_headroom(skb) < min_headroom || skb_header_cloned(skb)) {
+               int head_delta = SKB_DATA_ALIGN(min_headroom -
+                                               skb_headroom(skb) +
+                                               16);
 
-               if (skb->sk)
-                       skb_set_owner_w(new_skb, skb->sk);
-               consume_skb(skb);
-               skb = new_skb;
+               err = pskb_expand_head(skb, max_t(int, head_delta, 0),
+                                      0, GFP_ATOMIC);
+               if (min_headroom > dev->needed_headroom)
+                       dev->needed_headroom = min_headroom;
+               if (unlikely(err))
+                       goto tx_err_dst_release;
        }
 
        if (!fl6->flowi6_mark && ndst)
                ipv6_push_nfrag_opts(skb, &opt.ops, &proto, NULL);
        }
 
-       if (likely(!skb->encapsulation)) {
-               skb_reset_inner_headers(skb);
-               skb->encapsulation = 1;
-       }
+       err = iptunnel_handle_offloads(skb,
+                                      (tunnel->parms.o_flags & GRE_CSUM) ?
+                                      SKB_GSO_GRE_CSUM : SKB_GSO_GRE);
+       if (err)
+               goto tx_err_dst_release;
 
        skb_push(skb, gre_hlen);
        skb_reset_network_header(skb);
                        *ptr = tunnel->parms.o_key;
                        ptr--;
                }
-               if (tunnel->parms.o_flags&GRE_CSUM) {
+               if ((tunnel->parms.o_flags & GRE_CSUM) &&
+                   !(skb_shinfo(skb)->gso_type &
+                     (SKB_GSO_GRE | SKB_GSO_GRE_CSUM))) {
                        *ptr = 0;
                        *(__sum16 *)ptr = gre6_checksum(skb);
                }
        dev->features           |= GRE6_FEATURES;
        dev->hw_features        |= GRE6_FEATURES;
 
-       /* Can use a lockless transmit, unless we generate output sequences */
-       if (!(nt->parms.o_flags & GRE_SEQ))
+       if (!(nt->parms.o_flags & GRE_SEQ)) {
+               /* TCP segmentation offload is not supported when we
+                * generate output sequences.
+                */
+               dev->features    |= NETIF_F_GSO_SOFTWARE;
+               dev->hw_features |= NETIF_F_GSO_SOFTWARE;
+
+               /* Can use a lockless transmit, unless we generate
+                * output sequences
+                */
                dev->features |= NETIF_F_LLTX;
+       }
 
        err = register_netdevice(dev);
        if (err)