diff options
author | Paul Janzen <pcj@linux.sez.to> | 2006-01-16 16:52:13 -0700 |
---|---|---|
committer | Jeff Garzik <jgarzik@pobox.com> | 2006-01-17 07:23:37 -0500 |
commit | f7ea333765438232ac346a2f23cfec3e2012758f (patch) | |
tree | 9a1f685e3220e9b6b4170586e7f4b4d19fc6e753 /drivers/net | |
parent | b44cd572623cb6a931a947d9108595517fd945f8 (diff) | |
download | blackbird-op-linux-f7ea333765438232ac346a2f23cfec3e2012758f.tar.gz blackbird-op-linux-f7ea333765438232ac346a2f23cfec3e2012758f.zip |
[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>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/mv643xx_eth.c | 54 |
1 files changed, 31 insertions, 23 deletions
diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index 4afb954092a6..e01b03c7615e 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -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; |