diff options
author | Li Yang <leoli@freescale.com> | 2009-03-24 19:15:33 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-03-25 20:21:19 -0400 |
commit | 93c1285c5d92c31f9bcc20355f1e86e95bec165e (patch) | |
tree | ef24903fe05abdb9796ed4636d3a05f357b5c238 /drivers/net/gianfar.c | |
parent | 8ca51986be0c19c8a48ef8f36568c5a3c2c0ac50 (diff) |
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 8a51df045e84..9d81e7a48dba 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c | |||
@@ -1239,10 +1239,19 @@ static int gfar_enet_open(struct net_device *dev) | |||
1239 | return err; | 1239 | return err; |
1240 | } | 1240 | } |
1241 | 1241 | ||
1242 | static inline struct txfcb *gfar_add_fcb(struct sk_buff *skb) | 1242 | static inline struct txfcb *gfar_add_fcb(struct sk_buff **skbp) |
1243 | { | 1243 | { |
1244 | struct txfcb *fcb = (struct txfcb *)skb_push (skb, GMAC_FCB_LEN); | 1244 | struct txfcb *fcb; |
1245 | 1245 | struct sk_buff *skb = *skbp; | |
1246 | |||
1247 | if (unlikely(skb_headroom(skb) < GMAC_FCB_LEN)) { | ||
1248 | struct sk_buff *old_skb = skb; | ||
1249 | skb = skb_realloc_headroom(old_skb, GMAC_FCB_LEN); | ||
1250 | if (!skb) | ||
1251 | return NULL; | ||
1252 | dev_kfree_skb_any(old_skb); | ||
1253 | } | ||
1254 | fcb = (struct txfcb *)skb_push(skb, GMAC_FCB_LEN); | ||
1246 | cacheable_memzero(fcb, GMAC_FCB_LEN); | 1255 | cacheable_memzero(fcb, GMAC_FCB_LEN); |
1247 | 1256 | ||
1248 | return fcb; | 1257 | return fcb; |
@@ -1363,18 +1372,20 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) | |||
1363 | 1372 | ||
1364 | /* Set up checksumming */ | 1373 | /* Set up checksumming */ |
1365 | if (CHECKSUM_PARTIAL == skb->ip_summed) { | 1374 | if (CHECKSUM_PARTIAL == skb->ip_summed) { |
1366 | fcb = gfar_add_fcb(skb); | 1375 | fcb = gfar_add_fcb(&skb); |
1367 | lstatus |= BD_LFLAG(TXBD_TOE); | 1376 | if (likely(fcb != NULL)) { |
1368 | gfar_tx_checksum(skb, fcb); | 1377 | lstatus |= BD_LFLAG(TXBD_TOE); |
1378 | gfar_tx_checksum(skb, fcb); | ||
1379 | } | ||
1369 | } | 1380 | } |
1370 | 1381 | ||
1371 | if (priv->vlgrp && vlan_tx_tag_present(skb)) { | 1382 | if (priv->vlgrp && vlan_tx_tag_present(skb)) { |
1372 | if (unlikely(NULL == fcb)) { | 1383 | if (unlikely(NULL == fcb)) |
1373 | fcb = gfar_add_fcb(skb); | 1384 | fcb = gfar_add_fcb(&skb); |
1385 | if (likely(fcb != NULL)) { | ||
1374 | lstatus |= BD_LFLAG(TXBD_TOE); | 1386 | lstatus |= BD_LFLAG(TXBD_TOE); |
1387 | gfar_tx_vlan(skb, fcb); | ||
1375 | } | 1388 | } |
1376 | |||
1377 | gfar_tx_vlan(skb, fcb); | ||
1378 | } | 1389 | } |
1379 | 1390 | ||
1380 | /* setup the TxBD length and buffer pointer for the first BD */ | 1391 | /* setup the TxBD length and buffer pointer for the first BD */ |