summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/e1000/e1000_main.c22
-rw-r--r--drivers/net/tg3.c6
-rw-r--r--include/linux/skbuff.h19
3 files changed, 43 insertions, 4 deletions
diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c
index aa5ad41acf24..d6224c37bfcc 100644
--- a/drivers/net/e1000/e1000_main.c
+++ b/drivers/net/e1000/e1000_main.c
@@ -1522,7 +1522,7 @@ e1000_watchdog(unsigned long data)
#define E1000_TX_FLAGS_VLAN_MASK 0xffff0000
#define E1000_TX_FLAGS_VLAN_SHIFT 16
-static inline boolean_t
+static inline int
e1000_tso(struct e1000_adapter *adapter, struct sk_buff *skb)
{
#ifdef NETIF_F_TSO
@@ -1531,8 +1531,15 @@ e1000_tso(struct e1000_adapter *adapter, struct sk_buff *skb)
uint32_t cmd_length = 0;
uint16_t ipcse, tucse, mss;
uint8_t ipcss, ipcso, tucss, tucso, hdr_len;
+ int err;
if(skb_shinfo(skb)->tso_size) {
+ if (skb_header_cloned(skb)) {
+ err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
+ if (err)
+ return err;
+ }
+
hdr_len = ((skb->h.raw - skb->data) + (skb->h.th->doff << 2));
mss = skb_shinfo(skb)->tso_size;
skb->nh.iph->tot_len = 0;
@@ -1569,11 +1576,11 @@ e1000_tso(struct e1000_adapter *adapter, struct sk_buff *skb)
if(++i == adapter->tx_ring.count) i = 0;
adapter->tx_ring.next_to_use = i;
- return TRUE;
+ return 1;
}
#endif
- return FALSE;
+ return 0;
}
static inline boolean_t
@@ -1798,6 +1805,7 @@ e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
unsigned int nr_frags = 0;
unsigned int mss = 0;
int count = 0;
+ int tso;
unsigned int f;
len -= skb->data_len;
@@ -1869,7 +1877,13 @@ e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
first = adapter->tx_ring.next_to_use;
- if(likely(e1000_tso(adapter, skb)))
+ tso = e1000_tso(adapter, skb);
+ if (tso < 0) {
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+ }
+
+ if (likely(tso))
tx_flags |= E1000_TX_FLAGS_TSO;
else if(likely(e1000_tx_csum(adapter, skb)))
tx_flags |= E1000_TX_FLAGS_CSUM;
diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c
index b5931a0c7664..56420e7b1be3 100644
--- a/drivers/net/tg3.c
+++ b/drivers/net/tg3.c
@@ -3110,6 +3110,12 @@ static int tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
(mss = skb_shinfo(skb)->tso_size) != 0) {
int tcp_opt_len, ip_tcp_len;
+ if (skb_header_cloned(skb) &&
+ pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) {
+ dev_kfree_skb(skb);
+ goto out_unlock;
+ }
+
tcp_opt_len = ((skb->h.th->doff - 5) * 4);
ip_tcp_len = (skb->nh.iph->ihl * 4) + sizeof(struct tcphdr);
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index a8b55c4f4d2f..ca05008ba5b5 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -391,6 +391,25 @@ static inline int skb_cloned(const struct sk_buff *skb)
}
/**
+ * skb_header_cloned - is the header a clone
+ * @skb: buffer to check
+ *
+ * Returns true if modifying the header part of the buffer requires
+ * the data to be copied.
+ */
+static inline int skb_header_cloned(const struct sk_buff *skb)
+{
+ int dataref;
+
+ if (!skb->cloned)
+ return 0;
+
+ dataref = atomic_read(&skb_shinfo(skb)->dataref);
+ dataref = (dataref & SKB_DATAREF_MASK) - (dataref >> SKB_DATAREF_SHIFT);
+ return dataref != 1;
+}
+
+/**
* skb_header_release - release reference to header
* @skb: buffer to operate on
*