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)