From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755818AbdKGBML (ORCPT ); Mon, 6 Nov 2017 20:12:11 -0500 Received: from shadbolt.e.decadent.org.uk ([88.96.1.126]:52515 "EHLO shadbolt.e.decadent.org.uk" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755229AbdKFXyJ (ORCPT ); Mon, 6 Nov 2017 18:54:09 -0500 Content-Type: text/plain; charset="UTF-8" Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 From: Ben Hutchings To: linux-kernel@vger.kernel.org, stable@vger.kernel.org CC: akpm@linux-foundation.org, "Doug Berger" , "David S. Miller" , "Florian Fainelli" Date: Mon, 06 Nov 2017 23:03:02 +0000 Message-ID: X-Mailer: LinuxStableQueue (scripts by bwh) Subject: [PATCH 3.16 016/294] net: bcmgenet: Fix unmapping of fragments in bcmgenet_xmit() In-Reply-To: X-SA-Exim-Connect-IP: 2a02:8011:400e:2:6f00:88c8:c921:d332 X-SA-Exim-Mail-From: ben@decadent.org.uk X-SA-Exim-Scanned: No (on shadbolt.decadent.org.uk); SAEximRunCond expanded to false Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 3.16.50-rc1 review patch. If anyone has any objections, please let me know. ------------------ From: Doug Berger commit 876dbadd53a7102e2a84afc84ea2bd3ee6dc5636 upstream. In case we fail to map a single fragment, we would be leaving the transmit ring populated with stale entries. This commit introduces the helper function bcmgenet_put_txcb() which takes care of rewinding the per-ring write pointer back to where we left. It also consolidates the functionality of bcmgenet_xmit_single() and bcmgenet_xmit_frag() into the bcmgenet_xmit() function to make the unmapping of control blocks cleaner. Fixes: 1c1008c793fa ("net: bcmgenet: add main driver file") Suggested-by: Florian Fainelli Signed-off-by: Doug Berger Signed-off-by: David S. Miller [bwh: Backported to 3.16: - There's no tx_dma_failed statistic - Adjust context] Signed-off-by: Ben Hutchings --- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 191 +++++++++++-------------- 1 file changed, 85 insertions(+), 106 deletions(-) --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -900,6 +900,23 @@ static struct enet_cb *bcmgenet_get_txcb return tx_cb_ptr; } +static struct enet_cb *bcmgenet_put_txcb(struct bcmgenet_priv *priv, + struct bcmgenet_tx_ring *ring) +{ + struct enet_cb *tx_cb_ptr; + + tx_cb_ptr = ring->cbs; + tx_cb_ptr += ring->write_ptr - ring->cb_ptr; + + /* Rewinding local write pointer */ + if (ring->write_ptr == ring->cb_ptr) + ring->write_ptr = ring->end_ptr; + else + ring->write_ptr--; + + return tx_cb_ptr; +} + /* Simple helper to free a control block's resources */ static void bcmgenet_free_cb(struct enet_cb *cb) { @@ -1030,93 +1047,6 @@ static void bcmgenet_tx_reclaim_all(stru bcmgenet_tx_reclaim(dev, &priv->tx_rings[DESC_INDEX]); } -/* Transmits a single SKB (either head of a fragment or a single SKB) - * caller must hold priv->lock - */ -static int bcmgenet_xmit_single(struct net_device *dev, - struct sk_buff *skb, - u16 dma_desc_flags, - struct bcmgenet_tx_ring *ring) -{ - struct bcmgenet_priv *priv = netdev_priv(dev); - struct device *kdev = &priv->pdev->dev; - struct enet_cb *tx_cb_ptr; - unsigned int skb_len; - dma_addr_t mapping; - u32 length_status; - int ret; - - tx_cb_ptr = bcmgenet_get_txcb(priv, ring); - - if (unlikely(!tx_cb_ptr)) - BUG(); - - tx_cb_ptr->skb = skb; - - skb_len = skb_headlen(skb); - - mapping = dma_map_single(kdev, skb->data, skb_len, DMA_TO_DEVICE); - ret = dma_mapping_error(kdev, mapping); - if (ret) { - netif_err(priv, tx_err, dev, "Tx DMA map failed\n"); - dev_kfree_skb(skb); - return ret; - } - - dma_unmap_addr_set(tx_cb_ptr, dma_addr, mapping); - dma_unmap_len_set(tx_cb_ptr, dma_len, skb_len); - length_status = (skb_len << DMA_BUFLENGTH_SHIFT) | dma_desc_flags | - (priv->hw_params->qtag_mask << DMA_TX_QTAG_SHIFT) | - DMA_TX_APPEND_CRC; - - if (skb->ip_summed == CHECKSUM_PARTIAL) - length_status |= DMA_TX_DO_CSUM; - - dmadesc_set(priv, tx_cb_ptr->bd_addr, mapping, length_status); - - return 0; -} - -/* Transmit a SKB fragement */ -static int bcmgenet_xmit_frag(struct net_device *dev, - skb_frag_t *frag, - u16 dma_desc_flags, - struct bcmgenet_tx_ring *ring) -{ - struct bcmgenet_priv *priv = netdev_priv(dev); - struct device *kdev = &priv->pdev->dev; - struct enet_cb *tx_cb_ptr; - unsigned int frag_size; - dma_addr_t mapping; - int ret; - - tx_cb_ptr = bcmgenet_get_txcb(priv, ring); - - if (unlikely(!tx_cb_ptr)) - BUG(); - - tx_cb_ptr->skb = NULL; - - frag_size = skb_frag_size(frag); - - mapping = skb_frag_dma_map(kdev, frag, 0, frag_size, DMA_TO_DEVICE); - ret = dma_mapping_error(kdev, mapping); - if (ret) { - netif_err(priv, tx_err, dev, "%s: Tx DMA map failed\n", - __func__); - return ret; - } - - dma_unmap_addr_set(tx_cb_ptr, dma_addr, mapping); - dma_unmap_len_set(tx_cb_ptr, dma_len, frag_size); - - dmadesc_set(priv, tx_cb_ptr->bd_addr, mapping, - (frag_size << DMA_BUFLENGTH_SHIFT) | dma_desc_flags | - (priv->hw_params->qtag_mask << DMA_TX_QTAG_SHIFT)); - - return 0; -} - /* Reallocate the SKB to put enough headroom in front of it and insert * the transmit checksum offsets in the descriptors */ @@ -1182,11 +1112,16 @@ static int bcmgenet_put_tx_csum(struct n static netdev_tx_t bcmgenet_xmit(struct sk_buff *skb, struct net_device *dev) { struct bcmgenet_priv *priv = netdev_priv(dev); + struct device *kdev = &priv->pdev->dev; struct bcmgenet_tx_ring *ring = NULL; + struct enet_cb *tx_cb_ptr; struct netdev_queue *txq; unsigned long flags = 0; int nr_frags, index; - u16 dma_desc_flags; + dma_addr_t mapping; + unsigned int size; + skb_frag_t *frag; + u32 len_stat; int ret; int i; @@ -1235,26 +1170,48 @@ static netdev_tx_t bcmgenet_xmit(struct } } - dma_desc_flags = DMA_SOP; - if (nr_frags == 0) - dma_desc_flags |= DMA_EOP; - - /* Transmit single SKB or head of fragment list */ - ret = bcmgenet_xmit_single(dev, skb, dma_desc_flags, ring); - if (ret) { - ret = NETDEV_TX_OK; - goto out; - } + for (i = 0; i <= nr_frags; i++) { + tx_cb_ptr = bcmgenet_get_txcb(priv, ring); - /* xmit fragment */ - for (i = 0; i < nr_frags; i++) { - ret = bcmgenet_xmit_frag(dev, - &skb_shinfo(skb)->frags[i], - (i == nr_frags - 1) ? DMA_EOP : 0, ring); + if (unlikely(!tx_cb_ptr)) + BUG(); + + if (!i) { + /* Transmit single SKB or head of fragment list */ + tx_cb_ptr->skb = skb; + size = skb_headlen(skb); + mapping = dma_map_single(kdev, skb->data, size, + DMA_TO_DEVICE); + } else { + /* xmit fragment */ + tx_cb_ptr->skb = NULL; + frag = &skb_shinfo(skb)->frags[i - 1]; + size = skb_frag_size(frag); + mapping = skb_frag_dma_map(kdev, frag, 0, size, + DMA_TO_DEVICE); + } + + ret = dma_mapping_error(kdev, mapping); if (ret) { + netif_err(priv, tx_err, dev, "Tx DMA map failed\n"); ret = NETDEV_TX_OK; - goto out; + goto out_unmap_frags; } + dma_unmap_addr_set(tx_cb_ptr, dma_addr, mapping); + dma_unmap_len_set(tx_cb_ptr, dma_len, size); + + len_stat = (size << DMA_BUFLENGTH_SHIFT) | + (priv->hw_params->qtag_mask << DMA_TX_QTAG_SHIFT); + + if (!i) { + len_stat |= DMA_TX_APPEND_CRC | DMA_SOP; + if (skb->ip_summed == CHECKSUM_PARTIAL) + len_stat |= DMA_TX_DO_CSUM; + } + if (i == nr_frags) + len_stat |= DMA_EOP; + + dmadesc_set(priv, tx_cb_ptr->bd_addr, mapping, len_stat); } skb_tx_timestamp(skb); @@ -1276,6 +1233,30 @@ out: spin_unlock_irqrestore(&ring->lock, flags); return ret; + +out_unmap_frags: + /* Back up for failed control block mapping */ + bcmgenet_put_txcb(priv, ring); + + /* Unmap successfully mapped control blocks */ + while (i-- > 0) { + tx_cb_ptr = bcmgenet_put_txcb(priv, ring); + if (tx_cb_ptr->skb) + dma_unmap_single(kdev, + dma_unmap_addr(tx_cb_ptr, dma_addr), + dma_unmap_len(tx_cb_ptr, dma_len), + DMA_TO_DEVICE); + else + dma_unmap_page(kdev, + dma_unmap_addr(tx_cb_ptr, dma_addr), + dma_unmap_len(tx_cb_ptr, dma_len), + DMA_TO_DEVICE); + dma_unmap_addr_set(tx_cb_ptr, dma_addr, 0); + tx_cb_ptr->skb = NULL; + } + + dev_kfree_skb(skb); + goto out; } static struct sk_buff *bcmgenet_rx_refill(struct bcmgenet_priv *priv,