sunvnet: don't change gso data on clones
authorDavid L Stevens <david.stevens@oracle.com>
Wed, 11 Feb 2015 13:20:17 +0000 (08:20 -0500)
committerDavid S. Miller <davem@davemloft.net>
Thu, 12 Feb 2015 03:41:40 +0000 (19:41 -0800)
This patch unclones an skb for the case where the sunvnet driver needs to
change the segmentation size so that it doesn't interfere with TCP SACK's
use of them.

Signed-off-by: David L Stevens <david.stevens@oracle.com>
Acked-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/sun/sunvnet.c

index 2b10b85d8a0881ba584cbdf3ea337edbea0c448f..22e0cad1b4b5a21e0edc9215781c1052a9c6ef84 100644 (file)
@@ -1192,23 +1192,16 @@ static int vnet_handle_offloads(struct vnet_port *port, struct sk_buff *skb)
        skb_pull(skb, maclen);
 
        if (port->tso && gso_size < datalen) {
+               if (skb_unclone(skb, GFP_ATOMIC))
+                       goto out_dropped;
+
                /* segment to TSO size */
                skb_shinfo(skb)->gso_size = datalen;
                skb_shinfo(skb)->gso_segs = gso_segs;
-
-               segs = skb_gso_segment(skb, dev->features & ~NETIF_F_TSO);
-
-               /* restore gso_size & gso_segs */
-               skb_shinfo(skb)->gso_size = gso_size;
-               skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len - hlen,
-                                                        gso_size);
-       } else
-               segs = skb_gso_segment(skb, dev->features & ~NETIF_F_TSO);
-       if (IS_ERR(segs)) {
-               dev->stats.tx_dropped++;
-               dev_kfree_skb_any(skb);
-               return NETDEV_TX_OK;
        }
+       segs = skb_gso_segment(skb, dev->features & ~NETIF_F_TSO);
+       if (IS_ERR(segs))
+               goto out_dropped;
 
        skb_push(skb, maclen);
        skb_reset_mac_header(skb);
@@ -1246,6 +1239,10 @@ static int vnet_handle_offloads(struct vnet_port *port, struct sk_buff *skb)
        if (!(status & NETDEV_TX_MASK))
                dev_kfree_skb_any(skb);
        return status;
+out_dropped:
+       dev->stats.tx_dropped++;
+       dev_kfree_skb_any(skb);
+       return NETDEV_TX_OK;
 }
 
 static int vnet_start_xmit(struct sk_buff *skb, struct net_device *dev)