| From f1590670ce069eefeb93916391a67643e6ad1630 Mon Sep 17 00:00:00 2001 |
| From: Alexey Brodkin <Alexey.Brodkin@synopsys.com> |
| Date: Wed, 24 Jun 2015 11:47:41 +0300 |
| Subject: stmmac: troubleshoot unexpected bits in des0 & des1 |
| |
| From: Alexey Brodkin <Alexey.Brodkin@synopsys.com> |
| |
| commit f1590670ce069eefeb93916391a67643e6ad1630 upstream. |
| |
| Current implementation of descriptor init procedure only takes |
| care about setting/clearing ownership flag in "des0"/"des1" |
| fields while it is perfectly possible to get unexpected bits |
| set because of the following factors: |
| |
| [1] On driver probe underlying memory allocated with |
| dma_alloc_coherent() might not be zeroed and so |
| it will be filled with garbage. |
| |
| [2] During driver operation some bits could be set by SD/MMC |
| controller (for example error flags etc). |
| |
| And unexpected and/or randomly set flags in "des0"/"des1" |
| fields may lead to unpredictable behavior of GMAC DMA block. |
| |
| This change addresses both items above with: |
| |
| [1] Use of dma_zalloc_coherent() instead of simple |
| dma_alloc_coherent() to make sure allocated memory is |
| zeroed. That shouldn't affect performance because |
| this allocation only happens once on driver probe. |
| |
| [2] Do explicit zeroing of both "des0" and "des1" fields |
| of all buffer descriptors during initialization of |
| DMA transfer. |
| |
| And while at it fixed identation of dma_free_coherent() |
| counterpart as well. |
| |
| Signed-off-by: Alexey Brodkin <abrodkin@synopsys.com> |
| Cc: Giuseppe Cavallaro <peppe.cavallaro@st.com> |
| Cc: arc-linux-dev@synopsys.com |
| Cc: linux-kernel@vger.kernel.org |
| Cc: stable@vger.kernel.org |
| Cc: David Miller <davem@davemloft.net> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/net/ethernet/stmicro/stmmac/descs.h | 2 + |
| drivers/net/ethernet/stmicro/stmmac/enh_desc.c | 3 + |
| drivers/net/ethernet/stmicro/stmmac/norm_desc.c | 3 + |
| drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 44 +++++++++++----------- |
| 4 files changed, 28 insertions(+), 24 deletions(-) |
| |
| --- a/drivers/net/ethernet/stmicro/stmmac/descs.h |
| +++ b/drivers/net/ethernet/stmicro/stmmac/descs.h |
| @@ -158,6 +158,8 @@ struct dma_desc { |
| u32 buffer2_size:13; |
| u32 reserved4:3; |
| } etx; /* -- enhanced -- */ |
| + |
| + u64 all_flags; |
| } des01; |
| unsigned int des2; |
| unsigned int des3; |
| --- a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c |
| +++ b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c |
| @@ -240,6 +240,7 @@ static int enh_desc_get_rx_status(void * |
| static void enh_desc_init_rx_desc(struct dma_desc *p, int disable_rx_ic, |
| int mode, int end) |
| { |
| + p->des01.all_flags = 0; |
| p->des01.erx.own = 1; |
| p->des01.erx.buffer1_size = BUF_SIZE_8KiB - 1; |
| |
| @@ -254,7 +255,7 @@ static void enh_desc_init_rx_desc(struct |
| |
| static void enh_desc_init_tx_desc(struct dma_desc *p, int mode, int end) |
| { |
| - p->des01.etx.own = 0; |
| + p->des01.all_flags = 0; |
| if (mode == STMMAC_CHAIN_MODE) |
| ehn_desc_tx_set_on_chain(p, end); |
| else |
| --- a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c |
| +++ b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c |
| @@ -123,6 +123,7 @@ static int ndesc_get_rx_status(void *dat |
| static void ndesc_init_rx_desc(struct dma_desc *p, int disable_rx_ic, int mode, |
| int end) |
| { |
| + p->des01.all_flags = 0; |
| p->des01.rx.own = 1; |
| p->des01.rx.buffer1_size = BUF_SIZE_2KiB - 1; |
| |
| @@ -137,7 +138,7 @@ static void ndesc_init_rx_desc(struct dm |
| |
| static void ndesc_init_tx_desc(struct dma_desc *p, int mode, int end) |
| { |
| - p->des01.tx.own = 0; |
| + p->des01.all_flags = 0; |
| if (mode == STMMAC_CHAIN_MODE) |
| ndesc_tx_set_on_chain(p, end); |
| else |
| --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c |
| +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c |
| @@ -1192,41 +1192,41 @@ static int alloc_dma_desc_resources(stru |
| goto err_tx_skbuff; |
| |
| if (priv->extend_desc) { |
| - priv->dma_erx = dma_alloc_coherent(priv->device, rxsize * |
| - sizeof(struct |
| - dma_extended_desc), |
| - &priv->dma_rx_phy, |
| - GFP_KERNEL); |
| + priv->dma_erx = dma_zalloc_coherent(priv->device, rxsize * |
| + sizeof(struct |
| + dma_extended_desc), |
| + &priv->dma_rx_phy, |
| + GFP_KERNEL); |
| if (!priv->dma_erx) |
| goto err_dma; |
| |
| - priv->dma_etx = dma_alloc_coherent(priv->device, txsize * |
| - sizeof(struct |
| - dma_extended_desc), |
| - &priv->dma_tx_phy, |
| - GFP_KERNEL); |
| + priv->dma_etx = dma_zalloc_coherent(priv->device, txsize * |
| + sizeof(struct |
| + dma_extended_desc), |
| + &priv->dma_tx_phy, |
| + GFP_KERNEL); |
| if (!priv->dma_etx) { |
| dma_free_coherent(priv->device, priv->dma_rx_size * |
| - sizeof(struct dma_extended_desc), |
| - priv->dma_erx, priv->dma_rx_phy); |
| + sizeof(struct dma_extended_desc), |
| + priv->dma_erx, priv->dma_rx_phy); |
| goto err_dma; |
| } |
| } else { |
| - priv->dma_rx = dma_alloc_coherent(priv->device, rxsize * |
| - sizeof(struct dma_desc), |
| - &priv->dma_rx_phy, |
| - GFP_KERNEL); |
| + priv->dma_rx = dma_zalloc_coherent(priv->device, rxsize * |
| + sizeof(struct dma_desc), |
| + &priv->dma_rx_phy, |
| + GFP_KERNEL); |
| if (!priv->dma_rx) |
| goto err_dma; |
| |
| - priv->dma_tx = dma_alloc_coherent(priv->device, txsize * |
| - sizeof(struct dma_desc), |
| - &priv->dma_tx_phy, |
| - GFP_KERNEL); |
| + priv->dma_tx = dma_zalloc_coherent(priv->device, txsize * |
| + sizeof(struct dma_desc), |
| + &priv->dma_tx_phy, |
| + GFP_KERNEL); |
| if (!priv->dma_tx) { |
| dma_free_coherent(priv->device, priv->dma_rx_size * |
| - sizeof(struct dma_desc), |
| - priv->dma_rx, priv->dma_rx_phy); |
| + sizeof(struct dma_desc), |
| + priv->dma_rx, priv->dma_rx_phy); |
| goto err_dma; |
| } |
| } |