Skip to content

Commit

Permalink
Merge branch 'net-ll_temac-Bugfixes'
Browse files Browse the repository at this point in the history
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: David S. Miller <[email protected]>
  • Loading branch information
davem330 committed Feb 24, 2020
2 parents 6132c1d + 1d63b8d commit e4686c2
Show file tree
Hide file tree
Showing 2 changed files with 175 additions and 38 deletions.
4 changes: 4 additions & 0 deletions drivers/net/ethernet/xilinx/ll_temac.h
Original file line number Diff line number Diff line change
Expand Up @@ -375,10 +375,14 @@ struct temac_local {
int tx_bd_next;
int tx_bd_tail;
int rx_bd_ci;
int rx_bd_tail;

/* DMA channel control setup */
u32 tx_chnl_ctrl;
u32 rx_chnl_ctrl;
u8 coalesce_count_rx;

struct delayed_work restart_work;
};

/* Wrappers for temac_ior()/temac_iow() function pointers above */
Expand Down
209 changes: 171 additions & 38 deletions drivers/net/ethernet/xilinx/ll_temac_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
#include <linux/ip.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/dma-mapping.h>
#include <linux/processor.h>
#include <linux/platform_data/xilinx-ll-temac.h>
Expand Down Expand Up @@ -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,
XTE_MAX_JUMBO_FRAME_SIZE,
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].len = cpu_to_be32(XTE_MAX_JUMBO_FRAME_SIZE);
lp->rx_bd_v[i].app0 = cpu_to_be32(STS_CTRL_APP0_IRQONEND);
Expand All @@ -387,12 +390,13 @@ static int temac_dma_bd_init(struct net_device *ndev)
lp->tx_bd_next = 0;
lp->tx_bd_tail = 0;
lp->rx_bd_ci = 0;
lp->rx_bd_tail = RX_BD_NUM - 1;

/* Enable RX DMA transfers */
wmb();
lp->dma_out(lp, RX_CURDESC_PTR, lp->rx_bd_p);
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 */
lp->dma_out(lp, TX_CURDESC_PTR, lp->tx_bd_p);
Expand Down Expand Up @@ -788,6 +792,9 @@ static void temac_start_xmit_done(struct net_device *ndev)
stat = be32_to_cpu(cur_p->app0);
}

/* Matches barrier in temac_start_xmit */
smp_mb();

netif_wake_queue(ndev);
}

Expand Down Expand Up @@ -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];

if (temac_check_tx_bd_space(lp, num_frag + 1)) {
if (!netif_queue_stopped(ndev))
netif_stop_queue(ndev);
return NETDEV_TX_BUSY;
if (netif_queue_stopped(ndev))
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;
Expand All @@ -850,19 +867,44 @@ temac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
skb_dma_addr = dma_map_single(ndev->dev.parent, skb->data,
skb_headlen(skb), DMA_TO_DEVICE);
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);
ptr_to_txbd((void *)skb, cur_p);

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;

cur_p = &lp->tx_bd_v[lp->tx_bd_tail];
skb_dma_addr = dma_map_single(ndev->dev.parent,
skb_frag_address(frag),
skb_frag_size(frag),
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->len = cpu_to_be32(skb_frag_size(frag));
cur_p->app0 = 0;
Expand All @@ -884,31 +926,56 @@ temac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
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)
{
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;
int rx_bd;
bool update_tail = false;

spin_lock_irqsave(&lp->rx_lock, flags);

tail_p = lp->rx_bd_p + sizeof(*lp->rx_bd_v) * lp->rx_bd_ci;
cur_p = &lp->rx_bd_v[lp->rx_bd_ci];

bdstat = be32_to_cpu(cur_p->app0);
while ((bdstat & STS_CTRL_APP0_CMPLT)) {
/* Process all received buffers, passing them on network
* stack. After this, the buffer descriptors will be in an
* un-allocated stage, where no skb is allocated for it, and
* they are therefore not available for TEMAC/DMA.
*/
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];
length = be32_to_cpu(cur_p->app4) & 0x3FFF;
/* Loop over all completed buffer descriptors */
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);
/* 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->protocol = eth_type_trans(skb, ndev);
skb_checksum_none_assert(skb);
Expand All @@ -923,43 +990,102 @@ static void ll_temac_recv(struct net_device *ndev)
* (back) for proper IP checksum byte order
* (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;
}

if (!skb_defer_rx_timestamp(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_bytes += length;

new_skb = netdev_alloc_skb_ip_align(ndev,
XTE_MAX_JUMBO_FRAME_SIZE);
if (!new_skb) {
spin_unlock_irqrestore(&lp->rx_lock, flags);
return;
rx_bd = lp->rx_bd_ci;
if (++lp->rx_bd_ci >= RX_BD_NUM)
lp->rx_bd_ci = 0;
} while (rx_bd != lp->rx_bd_tail);

/* 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, new_skb->data,
skb_dma_addr = dma_map_single(ndev->dev.parent, skb->data,
XTE_MAX_JUMBO_FRAME_SIZE,
DMA_FROM_DEVICE);
cur_p->phys = cpu_to_be32(skb_dma_addr);
cur_p->len = cpu_to_be32(XTE_MAX_JUMBO_FRAME_SIZE);
lp->rx_skb[lp->rx_bd_ci] = new_skb;
if (WARN_ON_ONCE(dma_mapping_error(ndev->dev.parent,
skb_dma_addr))) {
dev_kfree_skb_any(skb);
break;
}

lp->rx_bd_ci++;
if (lp->rx_bd_ci >= RX_BD_NUM)
lp->rx_bd_ci = 0;
bd->phys = cpu_to_be32(skb_dma_addr);
bd->len = cpu_to_be32(XTE_MAX_JUMBO_FRAME_SIZE);
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];
bdstat = be32_to_cpu(cur_p->app0);
lp->rx_bd_tail = rx_bd;
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);
}

/* 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)
{
struct net_device *ndev = _ndev;
Expand Down Expand Up @@ -1052,6 +1178,8 @@ static int temac_stop(struct net_device *ndev)

dev_dbg(&ndev->dev, "temac_close()\n");

cancel_delayed_work_sync(&lp->restart_work);

free_irq(lp->tx_irq, ndev);
free_irq(lp->rx_irq, ndev);

Expand Down Expand Up @@ -1173,6 +1301,7 @@ static int temac_probe(struct platform_device *pdev)
lp->dev = &pdev->dev;
lp->options = XTE_OPTION_DEFAULTS;
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 */
if (pdata) {
Expand Down Expand Up @@ -1279,6 +1408,7 @@ static int temac_probe(struct platform_device *pdev)
*/
lp->tx_chnl_ctrl = 0x10220000;
lp->rx_chnl_ctrl = 0xff070000;
lp->coalesce_count_rx = 0x07;

/* Finished with the DMA node; drop the reference */
of_node_put(dma_np);
Expand Down Expand Up @@ -1310,11 +1440,14 @@ static int temac_probe(struct platform_device *pdev)
(pdata->tx_irq_count << 16);
else
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) |
(pdata->rx_irq_count << 16);
else
lp->coalesce_count_rx = pdata->rx_irq_count;
} else {
lp->rx_chnl_ctrl = 0xff070000;
lp->coalesce_count_rx = 0x07;
}
}

/* Error handle returned DMA RX and TX interrupts */
Expand Down

0 comments on commit e4686c2

Please sign in to comment.