]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
tg3: Fix 4k tx bd segmentation code
authorJoe Jin <joe.jin@oracle.com>
Tue, 15 May 2012 13:18:45 +0000 (21:18 +0800)
committerJoe Jin <joe.jin@oracle.com>
Wed, 16 May 2012 02:29:30 +0000 (10:29 +0800)
The new 4k tx bd segmentation code had a bug in the error cleanup path.
If the driver did not map all the physical fragments, the abort path
would wind up advancing the producer index beyond the point where the
setup code stopped.  This would ultimately turn into a tx recovery error
where the driver would expect the skb pointer to be set when it isn't.
This patch fixes the problem, and then makes the code a little easier to
understand.

(backport from commit b9e454826f22e17d1945bd282834c87aef8d0f95)
Signed-off-by: Matt Carlson <mcarlson@broadcom.com>
Reviewed-by: Michael Chan <mchan@broadcom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Joe Jin <joe.jin@oracle.com>
drivers/net/tg3.c

index cfbfe5a7166def695bf7852846cd073c48f68926..73382fa4d2325aae73c32d722687c6cd1a2f36fd 100644 (file)
@@ -6450,31 +6450,26 @@ static bool tg3_tx_frag_set(struct tg3_napi *tnapi, u32 *entry, u32 *budget,
                hwbug = 1;
 
        if (tg3_flag(tp, 4K_FIFO_LIMIT)) {
+               u32 prvidx = *entry;
                u32 tmp_flag = flags & ~TXD_FLAG_END;
-               while (len > TG3_TX_BD_DMA_MAX) {
+               while (len > TG3_TX_BD_DMA_MAX && *budget) {
                        u32 frag_len = TG3_TX_BD_DMA_MAX;
                        len -= TG3_TX_BD_DMA_MAX;
 
-                       if (len) {
-                               tnapi->tx_buffers[*entry].fragmented = true;
-                               /* Avoid the 8byte DMA problem */
-                               if (len <= 8) {
-                                       len += TG3_TX_BD_DMA_MAX / 2;
-                                       frag_len = TG3_TX_BD_DMA_MAX / 2;
-                               }
-                       } else
-                               tmp_flag = flags;
-
-                       if (*budget) {
-                               tg3_tx_set_bd(&tnapi->tx_ring[*entry], map,
-                                             frag_len, tmp_flag, mss, vlan);
-                               (*budget)--;
-                               *entry = NEXT_TX(*entry);
-                       } else {
-                               hwbug = 1;
-                               break;
+                       /* Avoid the 8byte DMA problem */
+                       if (len <= 8) {
+                               len += TG3_TX_BD_DMA_MAX / 2;
+                               frag_len = TG3_TX_BD_DMA_MAX / 2;
                        }
 
+                       tnapi->tx_buffers[*entry].fragmented = true;
+
+                       tg3_tx_set_bd(&tnapi->tx_ring[*entry], map,
+                                     frag_len, tmp_flag, mss, vlan);
+                       *budget -= 1;
+                       prvidx = *entry;
+                       *entry = NEXT_TX(*entry);
+
                        map += frag_len;
                }
 
@@ -6482,10 +6477,11 @@ static bool tg3_tx_frag_set(struct tg3_napi *tnapi, u32 *entry, u32 *budget,
                        if (*budget) {
                                tg3_tx_set_bd(&tnapi->tx_ring[*entry], map,
                                              len, flags, mss, vlan);
-                               (*budget)--;
+                               *budget -= 1;
                                *entry = NEXT_TX(*entry);
                        } else {
                                hwbug = 1;
+                               tnapi->tx_buffers[prvidx].fragmented = false;
                        }
                }
        } else {
@@ -6567,6 +6563,8 @@ static int tigon3_dma_hwbug_workaround(struct tg3_napi *tnapi,
                        dev_kfree_skb(new_skb);
                        ret = -1;
                } else {
+                       u32 save_entry = *entry;
+
                        base_flags |= TXD_FLAG_END;
 
                        tnapi->tx_buffers[*entry].skb = new_skb;
@@ -6576,7 +6574,7 @@ static int tigon3_dma_hwbug_workaround(struct tg3_napi *tnapi,
                        if (tg3_tx_frag_set(tnapi, entry, budget, new_addr,
                                            new_skb->len, base_flags,
                                            mss, vlan)) {
-                               tg3_tx_skb_unmap(tnapi, *entry, 0);
+                               tg3_tx_skb_unmap(tnapi, save_entry, 0);
                                dev_kfree_skb(new_skb);
                                ret = -1;
                        }
@@ -6796,11 +6794,14 @@ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
                        if (pci_dma_mapping_error(tp->pdev, mapping))
                                goto dma_error;
 
-                       if (tg3_tx_frag_set(tnapi, &entry, &budget, mapping,
+                       if (!budget ||
+                           tg3_tx_frag_set(tnapi, &entry, &budget, mapping,
                                            len, base_flags |
                                            ((i == last) ? TXD_FLAG_END : 0),
-                                           tmp_mss, vlan))
+                                           tmp_mss, vlan)) {
                                would_hit_hwbug = 1;
+                               break;
+                       }
                }
        }