aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/gianfar.c
diff options
context:
space:
mode:
authorStephen Hemminger <shemminger@vyatta.com>2009-03-27 03:38:45 -0400
committerDavid S. Miller <davem@davemloft.net>2009-03-27 03:38:45 -0400
commit54dc79fe0d895758bdaa1dcf8512d3d21263d105 (patch)
tree8a6e73c9e3a227f420bd258d538dd525f3955f9c /drivers/net/gianfar.c
parent83e0bbcbe2145f160fbaa109b0439dae7f4a38a9 (diff)
gianfar: fix headroom expansion code
The code that was added to increase headroom was wrong. It doesn't handle the case where gfar_add_fcb() changes the skb. Better to do check at start of transmit (outside of lock), where error handling is better anyway. Signed-off-by: Stephen Hemminger <shemminger@vyatta.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/gianfar.c')
-rw-r--r--drivers/net/gianfar.c46
1 files changed, 24 insertions, 22 deletions
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index 9d81e7a48dba..a7a67376615b 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -1239,19 +1239,9 @@ static int gfar_enet_open(struct net_device *dev)
1239 return err; 1239 return err;
1240} 1240}
1241 1241
1242static inline struct txfcb *gfar_add_fcb(struct sk_buff **skbp) 1242static inline struct txfcb *gfar_add_fcb(struct sk_buff *skb)
1243{ 1243{
1244 struct txfcb *fcb; 1244 struct txfcb *fcb = (struct txfcb *)skb_push(skb, GMAC_FCB_LEN);
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);
1255 cacheable_memzero(fcb, GMAC_FCB_LEN); 1245 cacheable_memzero(fcb, GMAC_FCB_LEN);
1256 1246
1257 return fcb; 1247 return fcb;
@@ -1320,6 +1310,20 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
1320 1310
1321 base = priv->tx_bd_base; 1311 base = priv->tx_bd_base;
1322 1312
1313 /* make space for additional header */
1314 if (skb_headroom(skb) < GMAC_FCB_LEN) {
1315 struct sk_buff *skb_new;
1316
1317 skb_new = skb_realloc_headroom(skb, GMAC_FCB_LEN);
1318 if (!skb_new) {
1319 dev->stats.tx_errors++;
1320 kfree(skb);
1321 return NETDEV_TX_OK;
1322 }
1323 kfree_skb(skb);
1324 skb = skb_new;
1325 }
1326
1323 /* total number of fragments in the SKB */ 1327 /* total number of fragments in the SKB */
1324 nr_frags = skb_shinfo(skb)->nr_frags; 1328 nr_frags = skb_shinfo(skb)->nr_frags;
1325 1329
@@ -1372,20 +1376,18 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
1372 1376
1373 /* Set up checksumming */ 1377 /* Set up checksumming */
1374 if (CHECKSUM_PARTIAL == skb->ip_summed) { 1378 if (CHECKSUM_PARTIAL == skb->ip_summed) {
1375 fcb = gfar_add_fcb(&skb); 1379 fcb = gfar_add_fcb(skb);
1376 if (likely(fcb != NULL)) { 1380 lstatus |= BD_LFLAG(TXBD_TOE);
1377 lstatus |= BD_LFLAG(TXBD_TOE); 1381 gfar_tx_checksum(skb, fcb);
1378 gfar_tx_checksum(skb, fcb);
1379 }
1380 } 1382 }
1381 1383
1382 if (priv->vlgrp && vlan_tx_tag_present(skb)) { 1384 if (priv->vlgrp && vlan_tx_tag_present(skb)) {
1383 if (unlikely(NULL == fcb)) 1385 if (unlikely(NULL == fcb)) {
1384 fcb = gfar_add_fcb(&skb); 1386 fcb = gfar_add_fcb(skb);
1385 if (likely(fcb != NULL)) {
1386 lstatus |= BD_LFLAG(TXBD_TOE); 1387 lstatus |= BD_LFLAG(TXBD_TOE);
1387 gfar_tx_vlan(skb, fcb);
1388 } 1388 }
1389
1390 gfar_tx_vlan(skb, fcb);
1389 } 1391 }
1390 1392
1391 /* setup the TxBD length and buffer pointer for the first BD */ 1393 /* setup the TxBD length and buffer pointer for the first BD */
@@ -1433,7 +1435,7 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
1433 /* Unlock priv */ 1435 /* Unlock priv */
1434 spin_unlock_irqrestore(&priv->txlock, flags); 1436 spin_unlock_irqrestore(&priv->txlock, flags);
1435 1437
1436 return 0; 1438 return NETDEV_TX_OK;
1437} 1439}
1438 1440
1439/* Stops the kernel queue, and halts the controller */ 1441/* Stops the kernel queue, and halts the controller */