[PATCH] mv643xx_eth: Fix handling of small, unaligned fragments
Fix handling of small, unaligned fragments. It also solves a potential deadlock if skb_linearize() returns -ENOMEM. Signed-off-by: Paul Janzen <pcj@linux.sez.to> Signed-off-by: Dale Farnsworth <dale@farnsworth.org> mv643xx_eth.c | 54 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 23 deletions(-) Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
This commit is contained in:
@@ -1093,6 +1093,25 @@ static int mv643xx_poll(struct net_device *dev, int *budget)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Hardware can't handle unaligned fragments smaller than 9 bytes.
|
||||||
|
* This helper function detects that case.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static inline unsigned int has_tiny_unaligned_frags(struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
unsigned int frag;
|
||||||
|
skb_frag_t *fragp;
|
||||||
|
|
||||||
|
for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) {
|
||||||
|
fragp = &skb_shinfo(skb)->frags[frag];
|
||||||
|
if (fragp->size <= 8 && fragp->page_offset & 0x7)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* mv643xx_eth_start_xmit
|
* mv643xx_eth_start_xmit
|
||||||
*
|
*
|
||||||
@@ -1136,12 +1155,19 @@ static int mv643xx_eth_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef MV643XX_CHECKSUM_OFFLOAD_TX
|
||||||
|
if (has_tiny_unaligned_frags(skb)) {
|
||||||
|
if ((skb_linearize(skb, GFP_ATOMIC) != 0)) {
|
||||||
|
stats->tx_dropped++;
|
||||||
|
printk(KERN_DEBUG "%s: failed to linearize tiny "
|
||||||
|
"unaligned fragment\n", dev->name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(&mp->lock, flags);
|
spin_lock_irqsave(&mp->lock, flags);
|
||||||
|
|
||||||
/* Update packet info data structure -- DMA owned, first last */
|
|
||||||
#ifdef MV643XX_CHECKSUM_OFFLOAD_TX
|
|
||||||
if (!skb_shinfo(skb)->nr_frags) {
|
if (!skb_shinfo(skb)->nr_frags) {
|
||||||
linear:
|
|
||||||
if (skb->ip_summed != CHECKSUM_HW) {
|
if (skb->ip_summed != CHECKSUM_HW) {
|
||||||
/* Errata BTS #50, IHL must be 5 if no HW checksum */
|
/* Errata BTS #50, IHL must be 5 if no HW checksum */
|
||||||
pkt_info.cmd_sts = ETH_TX_ENABLE_INTERRUPT |
|
pkt_info.cmd_sts = ETH_TX_ENABLE_INTERRUPT |
|
||||||
@@ -1183,26 +1209,6 @@ linear:
|
|||||||
} else {
|
} else {
|
||||||
unsigned int frag;
|
unsigned int frag;
|
||||||
|
|
||||||
/* Since hardware can't handle unaligned fragments smaller
|
|
||||||
* than 9 bytes, if we find any, we linearize the skb
|
|
||||||
* and start again. When I've seen it, it's always been
|
|
||||||
* the first frag (probably near the end of the page),
|
|
||||||
* but we check all frags to be safe.
|
|
||||||
*/
|
|
||||||
for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) {
|
|
||||||
skb_frag_t *fragp;
|
|
||||||
|
|
||||||
fragp = &skb_shinfo(skb)->frags[frag];
|
|
||||||
if (fragp->size <= 8 && fragp->page_offset & 0x7) {
|
|
||||||
skb_linearize(skb, GFP_ATOMIC);
|
|
||||||
printk(KERN_DEBUG "%s: unaligned tiny fragment"
|
|
||||||
"%d of %d, fixed\n",
|
|
||||||
dev->name, frag,
|
|
||||||
skb_shinfo(skb)->nr_frags);
|
|
||||||
goto linear;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* first frag which is skb header */
|
/* first frag which is skb header */
|
||||||
pkt_info.byte_cnt = skb_headlen(skb);
|
pkt_info.byte_cnt = skb_headlen(skb);
|
||||||
pkt_info.buf_ptr = dma_map_single(NULL, skb->data,
|
pkt_info.buf_ptr = dma_map_single(NULL, skb->data,
|
||||||
@@ -1288,6 +1294,8 @@ linear:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
spin_lock_irqsave(&mp->lock, flags);
|
||||||
|
|
||||||
pkt_info.cmd_sts = ETH_TX_ENABLE_INTERRUPT | ETH_TX_FIRST_DESC |
|
pkt_info.cmd_sts = ETH_TX_ENABLE_INTERRUPT | ETH_TX_FIRST_DESC |
|
||||||
ETH_TX_LAST_DESC;
|
ETH_TX_LAST_DESC;
|
||||||
pkt_info.l4i_chk = 0;
|
pkt_info.l4i_chk = 0;
|
||||||
|
Reference in New Issue
Block a user