[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:
Paul Janzen 2006-01-16 16:52:13 -07:00 committed by Jeff Garzik
parent b44cd57262
commit f7ea333765

View file

@ -1093,6 +1093,25 @@ static int mv643xx_poll(struct net_device *dev, int *budget)
}
#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
*
@ -1136,12 +1155,19 @@ static int mv643xx_eth_start_xmit(struct sk_buff *skb, struct net_device *dev)
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);
/* Update packet info data structure -- DMA owned, first last */
#ifdef MV643XX_CHECKSUM_OFFLOAD_TX
if (!skb_shinfo(skb)->nr_frags) {
linear:
if (skb->ip_summed != CHECKSUM_HW) {
/* Errata BTS #50, IHL must be 5 if no HW checksum */
pkt_info.cmd_sts = ETH_TX_ENABLE_INTERRUPT |
@ -1183,26 +1209,6 @@ linear:
} else {
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 */
pkt_info.byte_cnt = skb_headlen(skb);
pkt_info.buf_ptr = dma_map_single(NULL, skb->data,
@ -1288,6 +1294,8 @@ linear:
}
}
#else
spin_lock_irqsave(&mp->lock, flags);
pkt_info.cmd_sts = ETH_TX_ENABLE_INTERRUPT | ETH_TX_FIRST_DESC |
ETH_TX_LAST_DESC;
pkt_info.l4i_chk = 0;