diff options
| -rw-r--r-- | drivers/net/e1000/e1000_main.c | 22 | ||||
| -rw-r--r-- | drivers/net/tg3.c | 6 | ||||
| -rw-r--r-- | include/linux/skbuff.h | 19 |
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 * |
