Commit 0d795012 authored by Karl Beldan's avatar Karl Beldan Committed by Luis Henriques

net: mv643xx_eth: reclaim TX skbs only when released by the HW

commit 2c2a9cbd upstream.

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: 3ae8f4e0 ('net: mv643xx_eth: Implement software TSO')
Reported-by: default avatarSlawomir Gajzner <slawomir.gajzner@gmail.com>
Reported-by: default avatarJulien D'Ascenzio <jdascenzio@yahoo.fr>
Signed-off-by: default avatarKarl 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: default avatarDavid S. Miller <davem@davemloft.net>
Signed-off-by: default avatarLuis Henriques <luis.henriques@canonical.com>
parent 9c5425d7
...@@ -1047,7 +1047,6 @@ static int txq_reclaim(struct tx_queue *txq, int budget, int force) ...@@ -1047,7 +1047,6 @@ static int txq_reclaim(struct tx_queue *txq, int budget, int force)
int tx_index; int tx_index;
struct tx_desc *desc; struct tx_desc *desc;
u32 cmd_sts; u32 cmd_sts;
struct sk_buff *skb;
tx_index = txq->tx_used_desc; tx_index = txq->tx_used_desc;
desc = &txq->tx_desc_area[tx_index]; desc = &txq->tx_desc_area[tx_index];
...@@ -1066,19 +1065,22 @@ static int txq_reclaim(struct tx_queue *txq, int budget, int force) ...@@ -1066,19 +1065,22 @@ static int txq_reclaim(struct tx_queue *txq, int budget, int force)
reclaimed++; reclaimed++;
txq->tx_desc_count--; txq->tx_desc_count--;
skb = NULL; if (!IS_TSO_HEADER(txq, desc->buf_ptr))
if (cmd_sts & TX_LAST_DESC) dma_unmap_single(mp->dev->dev.parent, desc->buf_ptr,
skb = __skb_dequeue(&txq->tx_skb); 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) { if (cmd_sts & ERROR_SUMMARY) {
netdev_info(mp->dev, "tx error\n"); netdev_info(mp->dev, "tx error\n");
mp->dev->stats.tx_errors++; 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); __netif_tx_unlock_bh(nq);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment