Commit e4686c2d authored by David S. Miller's avatar David S. Miller

Merge branch 'net-ll_temac-Bugfixes'

Esben Haabendal says:

====================
net: ll_temac: Bugfixes

Fix a number of bugs which have been present since the first commit.

The bugs fixed in patch 1,2 and 4 have all been observed in real systems, and
was relatively easy to reproduce given an appropriate stress setup.

Changes since v1:

- Changed error handling of of dma_map_single() in temac_start_xmit() to drop
  packet instead of returning NETDEV_TX_BUSY.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 6132c1d9 1d63b8d6
...@@ -375,10 +375,14 @@ struct temac_local { ...@@ -375,10 +375,14 @@ struct temac_local {
int tx_bd_next; int tx_bd_next;
int tx_bd_tail; int tx_bd_tail;
int rx_bd_ci; int rx_bd_ci;
int rx_bd_tail;
/* DMA channel control setup */ /* DMA channel control setup */
u32 tx_chnl_ctrl; u32 tx_chnl_ctrl;
u32 rx_chnl_ctrl; u32 rx_chnl_ctrl;
u8 coalesce_count_rx;
struct delayed_work restart_work;
}; };
/* Wrappers for temac_ior()/temac_iow() function pointers above */ /* Wrappers for temac_ior()/temac_iow() function pointers above */
......
...@@ -51,6 +51,7 @@ ...@@ -51,6 +51,7 @@
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/processor.h> #include <linux/processor.h>
#include <linux/platform_data/xilinx-ll-temac.h> #include <linux/platform_data/xilinx-ll-temac.h>
...@@ -367,6 +368,8 @@ static int temac_dma_bd_init(struct net_device *ndev) ...@@ -367,6 +368,8 @@ static int temac_dma_bd_init(struct net_device *ndev)
skb_dma_addr = dma_map_single(ndev->dev.parent, skb->data, skb_dma_addr = dma_map_single(ndev->dev.parent, skb->data,
XTE_MAX_JUMBO_FRAME_SIZE, XTE_MAX_JUMBO_FRAME_SIZE,
DMA_FROM_DEVICE); DMA_FROM_DEVICE);
if (dma_mapping_error(ndev->dev.parent, skb_dma_addr))
goto out;
lp->rx_bd_v[i].phys = cpu_to_be32(skb_dma_addr); lp->rx_bd_v[i].phys = cpu_to_be32(skb_dma_addr);
lp->rx_bd_v[i].len = cpu_to_be32(XTE_MAX_JUMBO_FRAME_SIZE); lp->rx_bd_v[i].len = cpu_to_be32(XTE_MAX_JUMBO_FRAME_SIZE);
lp->rx_bd_v[i].app0 = cpu_to_be32(STS_CTRL_APP0_IRQONEND); lp->rx_bd_v[i].app0 = cpu_to_be32(STS_CTRL_APP0_IRQONEND);
...@@ -387,12 +390,13 @@ static int temac_dma_bd_init(struct net_device *ndev) ...@@ -387,12 +390,13 @@ static int temac_dma_bd_init(struct net_device *ndev)
lp->tx_bd_next = 0; lp->tx_bd_next = 0;
lp->tx_bd_tail = 0; lp->tx_bd_tail = 0;
lp->rx_bd_ci = 0; lp->rx_bd_ci = 0;
lp->rx_bd_tail = RX_BD_NUM - 1;
/* Enable RX DMA transfers */ /* Enable RX DMA transfers */
wmb(); wmb();
lp->dma_out(lp, RX_CURDESC_PTR, lp->rx_bd_p); lp->dma_out(lp, RX_CURDESC_PTR, lp->rx_bd_p);
lp->dma_out(lp, RX_TAILDESC_PTR, lp->dma_out(lp, RX_TAILDESC_PTR,
lp->rx_bd_p + (sizeof(*lp->rx_bd_v) * (RX_BD_NUM - 1))); lp->rx_bd_p + (sizeof(*lp->rx_bd_v) * lp->rx_bd_tail));
/* Prepare for TX DMA transfer */ /* Prepare for TX DMA transfer */
lp->dma_out(lp, TX_CURDESC_PTR, lp->tx_bd_p); lp->dma_out(lp, TX_CURDESC_PTR, lp->tx_bd_p);
...@@ -788,6 +792,9 @@ static void temac_start_xmit_done(struct net_device *ndev) ...@@ -788,6 +792,9 @@ static void temac_start_xmit_done(struct net_device *ndev)
stat = be32_to_cpu(cur_p->app0); stat = be32_to_cpu(cur_p->app0);
} }
/* Matches barrier in temac_start_xmit */
smp_mb();
netif_wake_queue(ndev); netif_wake_queue(ndev);
} }
...@@ -830,9 +837,19 @@ temac_start_xmit(struct sk_buff *skb, struct net_device *ndev) ...@@ -830,9 +837,19 @@ temac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
cur_p = &lp->tx_bd_v[lp->tx_bd_tail]; cur_p = &lp->tx_bd_v[lp->tx_bd_tail];
if (temac_check_tx_bd_space(lp, num_frag + 1)) { if (temac_check_tx_bd_space(lp, num_frag + 1)) {
if (!netif_queue_stopped(ndev)) if (netif_queue_stopped(ndev))
netif_stop_queue(ndev); return NETDEV_TX_BUSY;
return NETDEV_TX_BUSY;
netif_stop_queue(ndev);
/* Matches barrier in temac_start_xmit_done */
smp_mb();
/* Space might have just been freed - check again */
if (temac_check_tx_bd_space(lp, num_frag))
return NETDEV_TX_BUSY;
netif_wake_queue(ndev);
} }
cur_p->app0 = 0; cur_p->app0 = 0;
...@@ -850,12 +867,16 @@ temac_start_xmit(struct sk_buff *skb, struct net_device *ndev) ...@@ -850,12 +867,16 @@ temac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
skb_dma_addr = dma_map_single(ndev->dev.parent, skb->data, skb_dma_addr = dma_map_single(ndev->dev.parent, skb->data,
skb_headlen(skb), DMA_TO_DEVICE); skb_headlen(skb), DMA_TO_DEVICE);
cur_p->len = cpu_to_be32(skb_headlen(skb)); cur_p->len = cpu_to_be32(skb_headlen(skb));
if (WARN_ON_ONCE(dma_mapping_error(ndev->dev.parent, skb_dma_addr))) {
dev_kfree_skb_any(skb);
ndev->stats.tx_dropped++;
return NETDEV_TX_OK;
}
cur_p->phys = cpu_to_be32(skb_dma_addr); cur_p->phys = cpu_to_be32(skb_dma_addr);
ptr_to_txbd((void *)skb, cur_p); ptr_to_txbd((void *)skb, cur_p);
for (ii = 0; ii < num_frag; ii++) { for (ii = 0; ii < num_frag; ii++) {
lp->tx_bd_tail++; if (++lp->tx_bd_tail >= TX_BD_NUM)
if (lp->tx_bd_tail >= TX_BD_NUM)
lp->tx_bd_tail = 0; lp->tx_bd_tail = 0;
cur_p = &lp->tx_bd_v[lp->tx_bd_tail]; cur_p = &lp->tx_bd_v[lp->tx_bd_tail];
...@@ -863,6 +884,27 @@ temac_start_xmit(struct sk_buff *skb, struct net_device *ndev) ...@@ -863,6 +884,27 @@ temac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
skb_frag_address(frag), skb_frag_address(frag),
skb_frag_size(frag), skb_frag_size(frag),
DMA_TO_DEVICE); DMA_TO_DEVICE);
if (dma_mapping_error(ndev->dev.parent, skb_dma_addr)) {
if (--lp->tx_bd_tail < 0)
lp->tx_bd_tail = TX_BD_NUM - 1;
cur_p = &lp->tx_bd_v[lp->tx_bd_tail];
while (--ii >= 0) {
--frag;
dma_unmap_single(ndev->dev.parent,
be32_to_cpu(cur_p->phys),
skb_frag_size(frag),
DMA_TO_DEVICE);
if (--lp->tx_bd_tail < 0)
lp->tx_bd_tail = TX_BD_NUM - 1;
cur_p = &lp->tx_bd_v[lp->tx_bd_tail];
}
dma_unmap_single(ndev->dev.parent,
be32_to_cpu(cur_p->phys),
skb_headlen(skb), DMA_TO_DEVICE);
dev_kfree_skb_any(skb);
ndev->stats.tx_dropped++;
return NETDEV_TX_OK;
}
cur_p->phys = cpu_to_be32(skb_dma_addr); cur_p->phys = cpu_to_be32(skb_dma_addr);
cur_p->len = cpu_to_be32(skb_frag_size(frag)); cur_p->len = cpu_to_be32(skb_frag_size(frag));
cur_p->app0 = 0; cur_p->app0 = 0;
...@@ -884,31 +926,56 @@ temac_start_xmit(struct sk_buff *skb, struct net_device *ndev) ...@@ -884,31 +926,56 @@ temac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
return NETDEV_TX_OK; return NETDEV_TX_OK;
} }
static int ll_temac_recv_buffers_available(struct temac_local *lp)
{
int available;
if (!lp->rx_skb[lp->rx_bd_ci])
return 0;
available = 1 + lp->rx_bd_tail - lp->rx_bd_ci;
if (available <= 0)
available += RX_BD_NUM;
return available;
}
static void ll_temac_recv(struct net_device *ndev) static void ll_temac_recv(struct net_device *ndev)
{ {
struct temac_local *lp = netdev_priv(ndev); struct temac_local *lp = netdev_priv(ndev);
struct sk_buff *skb, *new_skb;
unsigned int bdstat;
struct cdmac_bd *cur_p;
dma_addr_t tail_p, skb_dma_addr;
int length;
unsigned long flags; unsigned long flags;
int rx_bd;
bool update_tail = false;
spin_lock_irqsave(&lp->rx_lock, flags); spin_lock_irqsave(&lp->rx_lock, flags);
tail_p = lp->rx_bd_p + sizeof(*lp->rx_bd_v) * lp->rx_bd_ci; /* Process all received buffers, passing them on network
cur_p = &lp->rx_bd_v[lp->rx_bd_ci]; * stack. After this, the buffer descriptors will be in an
* un-allocated stage, where no skb is allocated for it, and
bdstat = be32_to_cpu(cur_p->app0); * they are therefore not available for TEMAC/DMA.
while ((bdstat & STS_CTRL_APP0_CMPLT)) { */
do {
struct cdmac_bd *bd = &lp->rx_bd_v[lp->rx_bd_ci];
struct sk_buff *skb = lp->rx_skb[lp->rx_bd_ci];
unsigned int bdstat = be32_to_cpu(bd->app0);
int length;
/* While this should not normally happen, we can end
* here when GFP_ATOMIC allocations fail, and we
* therefore have un-allocated buffers.
*/
if (!skb)
break;
skb = lp->rx_skb[lp->rx_bd_ci]; /* Loop over all completed buffer descriptors */
length = be32_to_cpu(cur_p->app4) & 0x3FFF; if (!(bdstat & STS_CTRL_APP0_CMPLT))
break;
dma_unmap_single(ndev->dev.parent, be32_to_cpu(cur_p->phys), dma_unmap_single(ndev->dev.parent, be32_to_cpu(bd->phys),
XTE_MAX_JUMBO_FRAME_SIZE, DMA_FROM_DEVICE); XTE_MAX_JUMBO_FRAME_SIZE, DMA_FROM_DEVICE);
/* The buffer is not valid for DMA anymore */
bd->phys = 0;
bd->len = 0;
length = be32_to_cpu(bd->app4) & 0x3FFF;
skb_put(skb, length); skb_put(skb, length);
skb->protocol = eth_type_trans(skb, ndev); skb->protocol = eth_type_trans(skb, ndev);
skb_checksum_none_assert(skb); skb_checksum_none_assert(skb);
...@@ -923,43 +990,102 @@ static void ll_temac_recv(struct net_device *ndev) ...@@ -923,43 +990,102 @@ static void ll_temac_recv(struct net_device *ndev)
* (back) for proper IP checksum byte order * (back) for proper IP checksum byte order
* (be16). * (be16).
*/ */
skb->csum = htons(be32_to_cpu(cur_p->app3) & 0xFFFF); skb->csum = htons(be32_to_cpu(bd->app3) & 0xFFFF);
skb->ip_summed = CHECKSUM_COMPLETE; skb->ip_summed = CHECKSUM_COMPLETE;
} }
if (!skb_defer_rx_timestamp(skb)) if (!skb_defer_rx_timestamp(skb))
netif_rx(skb); netif_rx(skb);
/* The skb buffer is now owned by network stack above */
lp->rx_skb[lp->rx_bd_ci] = NULL;
ndev->stats.rx_packets++; ndev->stats.rx_packets++;
ndev->stats.rx_bytes += length; ndev->stats.rx_bytes += length;
new_skb = netdev_alloc_skb_ip_align(ndev, rx_bd = lp->rx_bd_ci;
XTE_MAX_JUMBO_FRAME_SIZE); if (++lp->rx_bd_ci >= RX_BD_NUM)
if (!new_skb) { lp->rx_bd_ci = 0;
spin_unlock_irqrestore(&lp->rx_lock, flags); } while (rx_bd != lp->rx_bd_tail);
return;
/* DMA operations will halt when the last buffer descriptor is
* processed (ie. the one pointed to by RX_TAILDESC_PTR).
* When that happens, no more interrupt events will be
* generated. No IRQ_COAL or IRQ_DLY, and not even an
* IRQ_ERR. To avoid stalling, we schedule a delayed work
* when there is a potential risk of that happening. The work
* will call this function, and thus re-schedule itself until
* enough buffers are available again.
*/
if (ll_temac_recv_buffers_available(lp) < lp->coalesce_count_rx)
schedule_delayed_work(&lp->restart_work, HZ / 1000);
/* Allocate new buffers for those buffer descriptors that were
* passed to network stack. Note that GFP_ATOMIC allocations
* can fail (e.g. when a larger burst of GFP_ATOMIC
* allocations occurs), so while we try to allocate all
* buffers in the same interrupt where they were processed, we
* continue with what we could get in case of allocation
* failure. Allocation of remaining buffers will be retried
* in following calls.
*/
while (1) {
struct sk_buff *skb;
struct cdmac_bd *bd;
dma_addr_t skb_dma_addr;
rx_bd = lp->rx_bd_tail + 1;
if (rx_bd >= RX_BD_NUM)
rx_bd = 0;
bd = &lp->rx_bd_v[rx_bd];
if (bd->phys)
break; /* All skb's allocated */
skb = netdev_alloc_skb_ip_align(ndev, XTE_MAX_JUMBO_FRAME_SIZE);
if (!skb) {
dev_warn(&ndev->dev, "skb alloc failed\n");
break;
} }
cur_p->app0 = cpu_to_be32(STS_CTRL_APP0_IRQONEND); skb_dma_addr = dma_map_single(ndev->dev.parent, skb->data,
skb_dma_addr = dma_map_single(ndev->dev.parent, new_skb->data,
XTE_MAX_JUMBO_FRAME_SIZE, XTE_MAX_JUMBO_FRAME_SIZE,
DMA_FROM_DEVICE); DMA_FROM_DEVICE);
cur_p->phys = cpu_to_be32(skb_dma_addr); if (WARN_ON_ONCE(dma_mapping_error(ndev->dev.parent,
cur_p->len = cpu_to_be32(XTE_MAX_JUMBO_FRAME_SIZE); skb_dma_addr))) {
lp->rx_skb[lp->rx_bd_ci] = new_skb; dev_kfree_skb_any(skb);
break;
}
lp->rx_bd_ci++; bd->phys = cpu_to_be32(skb_dma_addr);
if (lp->rx_bd_ci >= RX_BD_NUM) bd->len = cpu_to_be32(XTE_MAX_JUMBO_FRAME_SIZE);
lp->rx_bd_ci = 0; bd->app0 = cpu_to_be32(STS_CTRL_APP0_IRQONEND);
lp->rx_skb[rx_bd] = skb;
cur_p = &lp->rx_bd_v[lp->rx_bd_ci]; lp->rx_bd_tail = rx_bd;
bdstat = be32_to_cpu(cur_p->app0); update_tail = true;
}
/* Move tail pointer when buffers have been allocated */
if (update_tail) {
lp->dma_out(lp, RX_TAILDESC_PTR,
lp->rx_bd_p + sizeof(*lp->rx_bd_v) * lp->rx_bd_tail);
} }
lp->dma_out(lp, RX_TAILDESC_PTR, tail_p);
spin_unlock_irqrestore(&lp->rx_lock, flags); spin_unlock_irqrestore(&lp->rx_lock, flags);
} }
/* Function scheduled to ensure a restart in case of DMA halt
* condition caused by running out of buffer descriptors.
*/
static void ll_temac_restart_work_func(struct work_struct *work)
{
struct temac_local *lp = container_of(work, struct temac_local,
restart_work.work);
struct net_device *ndev = lp->ndev;
ll_temac_recv(ndev);
}
static irqreturn_t ll_temac_tx_irq(int irq, void *_ndev) static irqreturn_t ll_temac_tx_irq(int irq, void *_ndev)
{ {
struct net_device *ndev = _ndev; struct net_device *ndev = _ndev;
...@@ -1052,6 +1178,8 @@ static int temac_stop(struct net_device *ndev) ...@@ -1052,6 +1178,8 @@ static int temac_stop(struct net_device *ndev)
dev_dbg(&ndev->dev, "temac_close()\n"); dev_dbg(&ndev->dev, "temac_close()\n");
cancel_delayed_work_sync(&lp->restart_work);
free_irq(lp->tx_irq, ndev); free_irq(lp->tx_irq, ndev);
free_irq(lp->rx_irq, ndev); free_irq(lp->rx_irq, ndev);
...@@ -1173,6 +1301,7 @@ static int temac_probe(struct platform_device *pdev) ...@@ -1173,6 +1301,7 @@ static int temac_probe(struct platform_device *pdev)
lp->dev = &pdev->dev; lp->dev = &pdev->dev;
lp->options = XTE_OPTION_DEFAULTS; lp->options = XTE_OPTION_DEFAULTS;
spin_lock_init(&lp->rx_lock); spin_lock_init(&lp->rx_lock);
INIT_DELAYED_WORK(&lp->restart_work, ll_temac_restart_work_func);
/* Setup mutex for synchronization of indirect register access */ /* Setup mutex for synchronization of indirect register access */
if (pdata) { if (pdata) {
...@@ -1279,6 +1408,7 @@ static int temac_probe(struct platform_device *pdev) ...@@ -1279,6 +1408,7 @@ static int temac_probe(struct platform_device *pdev)
*/ */
lp->tx_chnl_ctrl = 0x10220000; lp->tx_chnl_ctrl = 0x10220000;
lp->rx_chnl_ctrl = 0xff070000; lp->rx_chnl_ctrl = 0xff070000;
lp->coalesce_count_rx = 0x07;
/* Finished with the DMA node; drop the reference */ /* Finished with the DMA node; drop the reference */
of_node_put(dma_np); of_node_put(dma_np);
...@@ -1310,11 +1440,14 @@ static int temac_probe(struct platform_device *pdev) ...@@ -1310,11 +1440,14 @@ static int temac_probe(struct platform_device *pdev)
(pdata->tx_irq_count << 16); (pdata->tx_irq_count << 16);
else else
lp->tx_chnl_ctrl = 0x10220000; lp->tx_chnl_ctrl = 0x10220000;
if (pdata->rx_irq_timeout || pdata->rx_irq_count) if (pdata->rx_irq_timeout || pdata->rx_irq_count) {
lp->rx_chnl_ctrl = (pdata->rx_irq_timeout << 24) | lp->rx_chnl_ctrl = (pdata->rx_irq_timeout << 24) |
(pdata->rx_irq_count << 16); (pdata->rx_irq_count << 16);
else lp->coalesce_count_rx = pdata->rx_irq_count;
} else {
lp->rx_chnl_ctrl = 0xff070000; lp->rx_chnl_ctrl = 0xff070000;
lp->coalesce_count_rx = 0x07;
}
} }
/* Error handle returned DMA RX and TX interrupts */ /* Error handle returned DMA RX and TX interrupts */
......
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