ag71xx: fix memory corruption issues on ar7240 on ethernet start/stop

When the DMA engine state gets corrupted due to a hardware issues, it
often won't stop rx until a full reset is issued. In that case the hardware
must keep a valid descriptor, otherwise it will write to random places in
system RAM, triggering random crashes. To fix this, keep a dummy descriptor
without a buffer that keeps the DMA engine in a sane state until the reset
is done

SVN-Revision: 27895
This commit is contained in:
Felix Fietkau 2011-08-04 17:36:27 +00:00
parent 500c3a1475
commit d2aeca6b6d
2 changed files with 19 additions and 3 deletions

View file

@ -148,6 +148,9 @@ struct ag71xx {
struct napi_struct napi; struct napi_struct napi;
u32 msg_enable; u32 msg_enable;
struct ag71xx_desc *stop_desc;
dma_addr_t stop_desc_dma;
struct ag71xx_ring rx_ring; struct ag71xx_ring rx_ring;
struct ag71xx_ring tx_ring; struct ag71xx_ring tx_ring;

View file

@ -374,8 +374,8 @@ static void ag71xx_dma_reset(struct ag71xx *ag)
mdelay(1); mdelay(1);
/* clear descriptor addresses */ /* clear descriptor addresses */
ag71xx_wr(ag, AG71XX_REG_TX_DESC, 0); ag71xx_wr(ag, AG71XX_REG_TX_DESC, ag->stop_desc_dma);
ag71xx_wr(ag, AG71XX_REG_RX_DESC, 0); ag71xx_wr(ag, AG71XX_REG_RX_DESC, ag->stop_desc_dma);
/* clear pending RX/TX interrupts */ /* clear pending RX/TX interrupts */
for (i = 0; i < 256; i++) { for (i = 0; i < 256; i++) {
@ -1085,6 +1085,16 @@ static int __devinit ag71xx_probe(struct platform_device *pdev)
ag->tx_ring.size = AG71XX_TX_RING_SIZE_DEFAULT; ag->tx_ring.size = AG71XX_TX_RING_SIZE_DEFAULT;
ag->rx_ring.size = AG71XX_RX_RING_SIZE_DEFAULT; ag->rx_ring.size = AG71XX_RX_RING_SIZE_DEFAULT;
ag->stop_desc = dma_alloc_coherent(NULL,
sizeof(struct ag71xx_desc), &ag->stop_desc_dma, GFP_KERNEL);
if (!ag->stop_desc)
goto err_free_irq;
ag->stop_desc->data = 0;
ag->stop_desc->ctrl = 0;
ag->stop_desc->next = (u32) ag->stop_desc_dma;
memcpy(dev->dev_addr, pdata->mac_addr, ETH_ALEN); memcpy(dev->dev_addr, pdata->mac_addr, ETH_ALEN);
netif_napi_add(dev, &ag->napi, ag71xx_poll, AG71XX_NAPI_WEIGHT); netif_napi_add(dev, &ag->napi, ag71xx_poll, AG71XX_NAPI_WEIGHT);
@ -1092,7 +1102,7 @@ static int __devinit ag71xx_probe(struct platform_device *pdev)
err = register_netdev(dev); err = register_netdev(dev);
if (err) { if (err) {
dev_err(&pdev->dev, "unable to register net device\n"); dev_err(&pdev->dev, "unable to register net device\n");
goto err_free_irq; goto err_free_desc;
} }
printk(KERN_INFO "%s: Atheros AG71xx at 0x%08lx, irq %d\n", printk(KERN_INFO "%s: Atheros AG71xx at 0x%08lx, irq %d\n",
@ -1120,6 +1130,9 @@ err_phy_disconnect:
ag71xx_phy_disconnect(ag); ag71xx_phy_disconnect(ag);
err_unregister_netdev: err_unregister_netdev:
unregister_netdev(dev); unregister_netdev(dev);
err_free_desc:
dma_free_coherent(NULL, sizeof(struct ag71xx_desc), ag->stop_desc,
ag->stop_desc_dma);
err_free_irq: err_free_irq:
free_irq(dev->irq, dev); free_irq(dev->irq, dev);
err_unmap_mii_ctrl: err_unmap_mii_ctrl: