diff options
author | Li Yang <leoli@freescale.com> | 2009-03-24 23:15:33 +0000 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-03-25 17:21:19 -0700 |
commit | 93c1285c5d92c31f9bcc20355f1e86e95bec165e (patch) | |
tree | ef24903fe05abdb9796ed4636d3a05f357b5c238 /drivers/net/gianfar.c | |
parent | 8ca51986be0c19c8a48ef8f36568c5a3c2c0ac50 (diff) | |
download | linux-3.10-93c1285c5d92c31f9bcc20355f1e86e95bec165e.tar.gz linux-3.10-93c1285c5d92c31f9bcc20355f1e86e95bec165e.tar.bz2 linux-3.10-93c1285c5d92c31f9bcc20355f1e86e95bec165e.zip |
gianfar: reallocate skb when headroom is not enough for fcb
Gianfar uses a hardware header FCB for offloading. However when used
with bridging or IP forwarding, TX skb might not have enough headroom
for the FCB. Reallocate skb for such cases.
Signed-off-by: Li Yang <leoli@freescale.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/gianfar.c')
-rw-r--r-- | drivers/net/gianfar.c | 31 |
1 files changed, 21 insertions, 10 deletions
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index 8a51df045e8..9d81e7a48db 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -1239,10 +1239,19 @@ static int gfar_enet_open(struct net_device *dev) return err; } -static inline struct txfcb *gfar_add_fcb(struct sk_buff *skb) +static inline struct txfcb *gfar_add_fcb(struct sk_buff **skbp) { - struct txfcb *fcb = (struct txfcb *)skb_push (skb, GMAC_FCB_LEN); - + struct txfcb *fcb; + struct sk_buff *skb = *skbp; + + if (unlikely(skb_headroom(skb) < GMAC_FCB_LEN)) { + struct sk_buff *old_skb = skb; + skb = skb_realloc_headroom(old_skb, GMAC_FCB_LEN); + if (!skb) + return NULL; + dev_kfree_skb_any(old_skb); + } + fcb = (struct txfcb *)skb_push(skb, GMAC_FCB_LEN); cacheable_memzero(fcb, GMAC_FCB_LEN); return fcb; @@ -1363,18 +1372,20 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) /* Set up checksumming */ if (CHECKSUM_PARTIAL == skb->ip_summed) { - fcb = gfar_add_fcb(skb); - lstatus |= BD_LFLAG(TXBD_TOE); - gfar_tx_checksum(skb, fcb); + fcb = gfar_add_fcb(&skb); + if (likely(fcb != NULL)) { + lstatus |= BD_LFLAG(TXBD_TOE); + gfar_tx_checksum(skb, fcb); + } } if (priv->vlgrp && vlan_tx_tag_present(skb)) { - if (unlikely(NULL == fcb)) { - fcb = gfar_add_fcb(skb); + if (unlikely(NULL == fcb)) + fcb = gfar_add_fcb(&skb); + if (likely(fcb != NULL)) { lstatus |= BD_LFLAG(TXBD_TOE); + gfar_tx_vlan(skb, fcb); } - - gfar_tx_vlan(skb, fcb); } /* setup the TxBD length and buffer pointer for the first BD */ |