]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
net: mv643xx_eth: reclaim TX skbs only when released by the HW
authorKarl Beldan <karl.beldan@rivierawaves.com>
Wed, 5 Nov 2014 14:32:59 +0000 (15:32 +0100)
committerDavid S. Miller <davem@davemloft.net>
Thu, 6 Nov 2014 19:54:39 +0000 (14:54 -0500)
ATM, txq_reclaim will dequeue and free an skb for each tx desc released
by the hw that has TX_LAST_DESC set. However, in case of TSO, each
hw desc embedding the last part of a segment has TX_LAST_DESC set,
losing the one-to-one 'last skb frag'/'TX_LAST_DESC set' correspondance,
which causes data corruption.

Fix this by checking TX_ENABLE_INTERRUPT instead of TX_LAST_DESC, and
warn when trying to dequeue from an empty txq (which can be symptomatic
of releasing skbs prematurely).

Fixes: 3ae8f4e0b98 ('net: mv643xx_eth: Implement software TSO')
Reported-by: Slawomir Gajzner <slawomir.gajzner@gmail.com>
Reported-by: Julien D'Ascenzio <jdascenzio@yahoo.fr>
Signed-off-by: Karl Beldan <karl.beldan@rivierawaves.com>
Cc: Ian Campbell <ijc@hellion.org.uk>
Cc: Eric Dumazet <eric.dumazet@gmail.com>
Cc: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
Cc: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/marvell/mv643xx_eth.c

index b151a949f352a20ec8e74b4f3a7b6bb194ce841c..d44560d1d268caae42143b674535db45c2784289 100644 (file)
@@ -1047,7 +1047,6 @@ static int txq_reclaim(struct tx_queue *txq, int budget, int force)
                int tx_index;
                struct tx_desc *desc;
                u32 cmd_sts;
-               struct sk_buff *skb;
 
                tx_index = txq->tx_used_desc;
                desc = &txq->tx_desc_area[tx_index];
@@ -1066,19 +1065,22 @@ static int txq_reclaim(struct tx_queue *txq, int budget, int force)
                reclaimed++;
                txq->tx_desc_count--;
 
-               skb = NULL;
-               if (cmd_sts & TX_LAST_DESC)
-                       skb = __skb_dequeue(&txq->tx_skb);
+               if (!IS_TSO_HEADER(txq, desc->buf_ptr))
+                       dma_unmap_single(mp->dev->dev.parent, desc->buf_ptr,
+                                        desc->byte_cnt, DMA_TO_DEVICE);
+
+               if (cmd_sts & TX_ENABLE_INTERRUPT) {
+                       struct sk_buff *skb = __skb_dequeue(&txq->tx_skb);
+
+                       if (!WARN_ON(!skb))
+                               dev_kfree_skb(skb);
+               }
 
                if (cmd_sts & ERROR_SUMMARY) {
                        netdev_info(mp->dev, "tx error\n");
                        mp->dev->stats.tx_errors++;
                }
 
-               if (!IS_TSO_HEADER(txq, desc->buf_ptr))
-                       dma_unmap_single(mp->dev->dev.parent, desc->buf_ptr,
-                                        desc->byte_cnt, DMA_TO_DEVICE);
-               dev_kfree_skb(skb);
        }
 
        __netif_tx_unlock_bh(nq);