mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-20 06:50:22 +07:00
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: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
e4686c2d08
@ -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 */
|
||||
|
@ -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>
|
||||
@ -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);
|
||||
@ -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);
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
@ -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_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];
|
||||
@ -863,6 +884,27 @@ temac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
|
||||
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;
|
||||
@ -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];
|
||||
/* 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;
|
||||
|
||||
bdstat = be32_to_cpu(cur_p->app0);
|
||||
while ((bdstat & STS_CTRL_APP0_CMPLT)) {
|
||||
/* 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);
|
||||
@ -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;
|
||||
@ -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);
|
||||
|
||||
@ -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) {
|
||||
@ -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);
|
||||
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user