NETIF_F_TSO6_BIT,               /* ... TCPv6 segmentation */
        NETIF_F_FSO_BIT,                /* ... FCoE segmentation */
        NETIF_F_GSO_GRE_BIT,            /* ... GRE with TSO */
+       NETIF_F_GSO_GRE_CSUM_BIT,       /* ... GRE with csum with TSO */
        NETIF_F_GSO_IPIP_BIT,           /* ... IPIP tunnel with TSO */
        NETIF_F_GSO_SIT_BIT,            /* ... SIT tunnel with TSO */
        NETIF_F_GSO_UDP_TUNNEL_BIT,     /* ... UDP TUNNEL with TSO */
 #define NETIF_F_RXFCS          __NETIF_F(RXFCS)
 #define NETIF_F_RXALL          __NETIF_F(RXALL)
 #define NETIF_F_GSO_GRE                __NETIF_F(GSO_GRE)
+#define NETIF_F_GSO_GRE_CSUM   __NETIF_F(GSO_GRE_CSUM)
 #define NETIF_F_GSO_IPIP       __NETIF_F(GSO_IPIP)
 #define NETIF_F_GSO_SIT                __NETIF_F(GSO_SIT)
 #define NETIF_F_GSO_UDP_TUNNEL __NETIF_F(GSO_UDP_TUNNEL)
 
        SKB_GSO_MPLS = 1 << 10,
 
        SKB_GSO_UDP_TUNNEL_CSUM = 1 << 11,
+
+       SKB_GSO_GRE_CSUM = 1 << 12,
 };
 
 #if BITS_PER_LONG > 32
 
                      int hdr_len);
 
 static inline struct sk_buff *gre_handle_offloads(struct sk_buff *skb,
-                                                 bool gre_csum)
+                                                 bool csum)
 {
-       return iptunnel_handle_offloads(skb, gre_csum, SKB_GSO_GRE);
+       return iptunnel_handle_offloads(skb, csum,
+                                       csum ? SKB_GSO_GRE_CSUM : SKB_GSO_GRE);
 }
 
 
 
                       SKB_GSO_DODGY |
                       SKB_GSO_TCP_ECN |
                       SKB_GSO_GRE |
+                      SKB_GSO_GRE_CSUM |
                       SKB_GSO_IPIP |
                       SKB_GSO_SIT |
                       SKB_GSO_TCPV6 |
 
                        ptr--;
                }
                if (tpi->flags&TUNNEL_CSUM &&
-                   !(skb_shinfo(skb)->gso_type & SKB_GSO_GRE)) {
+                   !(skb_shinfo(skb)->gso_type &
+                     (SKB_GSO_GRE|SKB_GSO_GRE_CSUM))) {
                        *ptr = 0;
                        *(__sum16 *)ptr = csum_fold(skb_checksum(skb, 0,
                                                                 skb->len, 0));
 
                                  SKB_GSO_DODGY |
                                  SKB_GSO_TCP_ECN |
                                  SKB_GSO_GRE |
+                                 SKB_GSO_GRE_CSUM |
                                  SKB_GSO_IPIP)))
                goto out;
 
                goto out;
 
        csum = !!(greh->flags & GRE_CSUM);
+       if (csum)
+               skb->encap_hdr_csum = 1;
 
        if (unlikely(!pskb_may_pull(skb, ghl)))
                goto out;
                                }
                        }
 
-                       greh = (struct gre_base_hdr *)(skb->data);
+                       skb_reset_transport_header(skb);
+
+                       greh = (struct gre_base_hdr *)
+                           skb_transport_header(skb);
                        pcsum = (__be32 *)(greh + 1);
                        *pcsum = 0;
-                       *(__sum16 *)pcsum = csum_fold(skb_checksum(skb, 0, skb->len, 0));
+                       *(__sum16 *)pcsum = gso_make_checksum(skb, 0);
                }
                __skb_push(skb, tnl_hlen - ghl);
 
 
                               SKB_GSO_TCP_ECN |
                               SKB_GSO_TCPV6 |
                               SKB_GSO_GRE |
+                              SKB_GSO_GRE_CSUM |
                               SKB_GSO_IPIP |
                               SKB_GSO_SIT |
                               SKB_GSO_MPLS |
 
                                      SKB_GSO_UDP_TUNNEL |
                                      SKB_GSO_UDP_TUNNEL_CSUM |
                                      SKB_GSO_IPIP |
-                                     SKB_GSO_GRE | SKB_GSO_MPLS) ||
+                                     SKB_GSO_GRE | SKB_GSO_GRE_CSUM |
+                                     SKB_GSO_MPLS) ||
                             !(type & (SKB_GSO_UDP))))
                        goto out;
 
 
                       SKB_GSO_DODGY |
                       SKB_GSO_TCP_ECN |
                       SKB_GSO_GRE |
+                      SKB_GSO_GRE_CSUM |
                       SKB_GSO_IPIP |
                       SKB_GSO_SIT |
                       SKB_GSO_UDP_TUNNEL |
 
                                      SKB_GSO_UDP_TUNNEL |
                                      SKB_GSO_UDP_TUNNEL_CSUM |
                                      SKB_GSO_GRE |
+                                     SKB_GSO_GRE_CSUM |
                                      SKB_GSO_IPIP |
                                      SKB_GSO_SIT |
                                      SKB_GSO_MPLS) ||
 
                                  SKB_GSO_DODGY |
                                  SKB_GSO_TCP_ECN |
                                  SKB_GSO_GRE |
+                                 SKB_GSO_GRE_CSUM |
                                  SKB_GSO_IPIP |
                                  SKB_GSO_MPLS)))
                goto out;