net: mvneta: fix transmit path dma-unmapping on error
authorRussell King (Oracle) <rmk+kernel@armlinux.org.uk>
Wed, 10 May 2023 10:15:42 +0000 (11:15 +0100)
committerPaolo Abeni <pabeni@redhat.com>
Thu, 11 May 2023 11:05:07 +0000 (13:05 +0200)
The transmit code assumes that the transmit descriptors that are used
begin with the first descriptor in the ring, but this may not be the
case. Fix this by providing a new function that dma-unmaps a range of
numbered descriptor entries, and use that to do the unmapping.

Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
drivers/net/ethernet/marvell/mvneta.c

index 2cad76d..62400ff 100644 (file)
@@ -2714,14 +2714,40 @@ mvneta_tso_put_data(struct net_device *dev, struct mvneta_tx_queue *txq,
        return 0;
 }
 
+static void mvneta_release_descs(struct mvneta_port *pp,
+                                struct mvneta_tx_queue *txq,
+                                int first, int num)
+{
+       int desc_idx, i;
+
+       desc_idx = first + num;
+       if (desc_idx >= txq->size)
+               desc_idx -= txq->size;
+
+       for (i = num; i >= 0; i--) {
+               struct mvneta_tx_desc *tx_desc = txq->descs + desc_idx;
+
+               if (!IS_TSO_HEADER(txq, tx_desc->buf_phys_addr))
+                       dma_unmap_single(pp->dev->dev.parent,
+                                        tx_desc->buf_phys_addr,
+                                        tx_desc->data_size,
+                                        DMA_TO_DEVICE);
+
+               mvneta_txq_desc_put(txq);
+
+               if (desc_idx == 0)
+                       desc_idx = txq->size;
+               desc_idx -= 1;
+       }
+}
+
 static int mvneta_tx_tso(struct sk_buff *skb, struct net_device *dev,
                         struct mvneta_tx_queue *txq)
 {
        int hdr_len, total_len, data_left;
-       int desc_count = 0;
+       int first_desc, desc_count = 0;
        struct mvneta_port *pp = netdev_priv(dev);
        struct tso_t tso;
-       int i;
 
        /* Count needed descriptors */
        if ((txq->count + tso_count_descs(skb)) >= txq->size)
@@ -2732,6 +2758,8 @@ static int mvneta_tx_tso(struct sk_buff *skb, struct net_device *dev,
                return 0;
        }
 
+       first_desc = txq->txq_put_index;
+
        /* Initialize the TSO handler, and prepare the first payload */
        hdr_len = tso_start(skb, &tso);
 
@@ -2772,15 +2800,7 @@ err_release:
        /* Release all used data descriptors; header descriptors must not
         * be DMA-unmapped.
         */
-       for (i = desc_count - 1; i >= 0; i--) {
-               struct mvneta_tx_desc *tx_desc = txq->descs + i;
-               if (!IS_TSO_HEADER(txq, tx_desc->buf_phys_addr))
-                       dma_unmap_single(pp->dev->dev.parent,
-                                        tx_desc->buf_phys_addr,
-                                        tx_desc->data_size,
-                                        DMA_TO_DEVICE);
-               mvneta_txq_desc_put(txq);
-       }
+       mvneta_release_descs(pp, txq, first_desc, desc_count - 1);
        return 0;
 }
 
@@ -2790,6 +2810,7 @@ static int mvneta_tx_frag_process(struct mvneta_port *pp, struct sk_buff *skb,
 {
        struct mvneta_tx_desc *tx_desc;
        int i, nr_frags = skb_shinfo(skb)->nr_frags;
+       int first_desc = txq->txq_put_index;
 
        for (i = 0; i < nr_frags; i++) {
                struct mvneta_tx_buf *buf = &txq->buf[txq->txq_put_index];
@@ -2828,15 +2849,7 @@ error:
        /* Release all descriptors that were used to map fragments of
         * this packet, as well as the corresponding DMA mappings
         */
-       for (i = i - 1; i >= 0; i--) {
-               tx_desc = txq->descs + i;
-               dma_unmap_single(pp->dev->dev.parent,
-                                tx_desc->buf_phys_addr,
-                                tx_desc->data_size,
-                                DMA_TO_DEVICE);
-               mvneta_txq_desc_put(txq);
-       }
-
+       mvneta_release_descs(pp, txq, first_desc, i - 1);
        return -ENOMEM;
 }