net: bcmgenet: synchronize irq0 status between the isr and task

Add a spinlock to ensure that irq0_stat is not unintentionally altered
as the result of preemption.  Also removed unserviced irq0 interrupts
and removed irq1_stat since there is no bottom half service for those
interrupts.

Fixes: 1c1008c793 ("net: bcmgenet: add main driver file")
Signed-off-by: Doug Berger <opendmb@gmail.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Doug Berger 2017-03-09 16:58:47 -08:00 коммит произвёл David S. Miller
Родитель 7627409cc4
Коммит 07c52d6a0b
2 изменённых файлов: 44 добавлений и 35 удалений

Просмотреть файл

@ -2506,24 +2506,28 @@ static int bcmgenet_init_dma(struct bcmgenet_priv *priv)
/* Interrupt bottom half */ /* Interrupt bottom half */
static void bcmgenet_irq_task(struct work_struct *work) static void bcmgenet_irq_task(struct work_struct *work)
{ {
unsigned long flags;
unsigned int status;
struct bcmgenet_priv *priv = container_of( struct bcmgenet_priv *priv = container_of(
work, struct bcmgenet_priv, bcmgenet_irq_work); work, struct bcmgenet_priv, bcmgenet_irq_work);
netif_dbg(priv, intr, priv->dev, "%s\n", __func__); netif_dbg(priv, intr, priv->dev, "%s\n", __func__);
if (priv->irq0_stat & UMAC_IRQ_MPD_R) { spin_lock_irqsave(&priv->lock, flags);
priv->irq0_stat &= ~UMAC_IRQ_MPD_R; status = priv->irq0_stat;
priv->irq0_stat = 0;
spin_unlock_irqrestore(&priv->lock, flags);
if (status & UMAC_IRQ_MPD_R) {
netif_dbg(priv, wol, priv->dev, netif_dbg(priv, wol, priv->dev,
"magic packet detected, waking up\n"); "magic packet detected, waking up\n");
bcmgenet_power_up(priv, GENET_POWER_WOL_MAGIC); bcmgenet_power_up(priv, GENET_POWER_WOL_MAGIC);
} }
/* Link UP/DOWN event */ /* Link UP/DOWN event */
if (priv->irq0_stat & UMAC_IRQ_LINK_EVENT) { if (status & UMAC_IRQ_LINK_EVENT)
phy_mac_interrupt(priv->phydev, phy_mac_interrupt(priv->phydev,
!!(priv->irq0_stat & UMAC_IRQ_LINK_UP)); !!(status & UMAC_IRQ_LINK_UP));
priv->irq0_stat &= ~UMAC_IRQ_LINK_EVENT;
}
} }
/* bcmgenet_isr1: handle Rx and Tx priority queues */ /* bcmgenet_isr1: handle Rx and Tx priority queues */
@ -2532,22 +2536,21 @@ static irqreturn_t bcmgenet_isr1(int irq, void *dev_id)
struct bcmgenet_priv *priv = dev_id; struct bcmgenet_priv *priv = dev_id;
struct bcmgenet_rx_ring *rx_ring; struct bcmgenet_rx_ring *rx_ring;
struct bcmgenet_tx_ring *tx_ring; struct bcmgenet_tx_ring *tx_ring;
unsigned int index; unsigned int index, status;
/* Save irq status for bottom-half processing. */ /* Read irq status */
priv->irq1_stat = status = bcmgenet_intrl2_1_readl(priv, INTRL2_CPU_STAT) &
bcmgenet_intrl2_1_readl(priv, INTRL2_CPU_STAT) &
~bcmgenet_intrl2_1_readl(priv, INTRL2_CPU_MASK_STATUS); ~bcmgenet_intrl2_1_readl(priv, INTRL2_CPU_MASK_STATUS);
/* clear interrupts */ /* clear interrupts */
bcmgenet_intrl2_1_writel(priv, priv->irq1_stat, INTRL2_CPU_CLEAR); bcmgenet_intrl2_1_writel(priv, status, INTRL2_CPU_CLEAR);
netif_dbg(priv, intr, priv->dev, netif_dbg(priv, intr, priv->dev,
"%s: IRQ=0x%x\n", __func__, priv->irq1_stat); "%s: IRQ=0x%x\n", __func__, status);
/* Check Rx priority queue interrupts */ /* Check Rx priority queue interrupts */
for (index = 0; index < priv->hw_params->rx_queues; index++) { for (index = 0; index < priv->hw_params->rx_queues; index++) {
if (!(priv->irq1_stat & BIT(UMAC_IRQ1_RX_INTR_SHIFT + index))) if (!(status & BIT(UMAC_IRQ1_RX_INTR_SHIFT + index)))
continue; continue;
rx_ring = &priv->rx_rings[index]; rx_ring = &priv->rx_rings[index];
@ -2560,7 +2563,7 @@ static irqreturn_t bcmgenet_isr1(int irq, void *dev_id)
/* Check Tx priority queue interrupts */ /* Check Tx priority queue interrupts */
for (index = 0; index < priv->hw_params->tx_queues; index++) { for (index = 0; index < priv->hw_params->tx_queues; index++) {
if (!(priv->irq1_stat & BIT(index))) if (!(status & BIT(index)))
continue; continue;
tx_ring = &priv->tx_rings[index]; tx_ring = &priv->tx_rings[index];
@ -2580,19 +2583,20 @@ static irqreturn_t bcmgenet_isr0(int irq, void *dev_id)
struct bcmgenet_priv *priv = dev_id; struct bcmgenet_priv *priv = dev_id;
struct bcmgenet_rx_ring *rx_ring; struct bcmgenet_rx_ring *rx_ring;
struct bcmgenet_tx_ring *tx_ring; struct bcmgenet_tx_ring *tx_ring;
unsigned int status;
unsigned long flags;
/* Save irq status for bottom-half processing. */ /* Read irq status */
priv->irq0_stat = status = bcmgenet_intrl2_0_readl(priv, INTRL2_CPU_STAT) &
bcmgenet_intrl2_0_readl(priv, INTRL2_CPU_STAT) &
~bcmgenet_intrl2_0_readl(priv, INTRL2_CPU_MASK_STATUS); ~bcmgenet_intrl2_0_readl(priv, INTRL2_CPU_MASK_STATUS);
/* clear interrupts */ /* clear interrupts */
bcmgenet_intrl2_0_writel(priv, priv->irq0_stat, INTRL2_CPU_CLEAR); bcmgenet_intrl2_0_writel(priv, status, INTRL2_CPU_CLEAR);
netif_dbg(priv, intr, priv->dev, netif_dbg(priv, intr, priv->dev,
"IRQ=0x%x\n", priv->irq0_stat); "IRQ=0x%x\n", status);
if (priv->irq0_stat & UMAC_IRQ_RXDMA_DONE) { if (status & UMAC_IRQ_RXDMA_DONE) {
rx_ring = &priv->rx_rings[DESC_INDEX]; rx_ring = &priv->rx_rings[DESC_INDEX];
if (likely(napi_schedule_prep(&rx_ring->napi))) { if (likely(napi_schedule_prep(&rx_ring->napi))) {
@ -2601,7 +2605,7 @@ static irqreturn_t bcmgenet_isr0(int irq, void *dev_id)
} }
} }
if (priv->irq0_stat & UMAC_IRQ_TXDMA_DONE) { if (status & UMAC_IRQ_TXDMA_DONE) {
tx_ring = &priv->tx_rings[DESC_INDEX]; tx_ring = &priv->tx_rings[DESC_INDEX];
if (likely(napi_schedule_prep(&tx_ring->napi))) { if (likely(napi_schedule_prep(&tx_ring->napi))) {
@ -2610,20 +2614,21 @@ static irqreturn_t bcmgenet_isr0(int irq, void *dev_id)
} }
} }
if (priv->irq0_stat & (UMAC_IRQ_PHY_DET_R | if ((priv->hw_params->flags & GENET_HAS_MDIO_INTR) &&
UMAC_IRQ_PHY_DET_F | status & (UMAC_IRQ_MDIO_DONE | UMAC_IRQ_MDIO_ERROR)) {
UMAC_IRQ_LINK_EVENT | wake_up(&priv->wq);
UMAC_IRQ_HFB_SM |
UMAC_IRQ_HFB_MM |
UMAC_IRQ_MPD_R)) {
/* all other interested interrupts handled in bottom half */
schedule_work(&priv->bcmgenet_irq_work);
} }
if ((priv->hw_params->flags & GENET_HAS_MDIO_INTR) && /* all other interested interrupts handled in bottom half */
priv->irq0_stat & (UMAC_IRQ_MDIO_DONE | UMAC_IRQ_MDIO_ERROR)) { status &= (UMAC_IRQ_LINK_EVENT |
priv->irq0_stat &= ~(UMAC_IRQ_MDIO_DONE | UMAC_IRQ_MDIO_ERROR); UMAC_IRQ_MPD_R);
wake_up(&priv->wq); if (status) {
/* Save irq status for bottom-half processing. */
spin_lock_irqsave(&priv->lock, flags);
priv->irq0_stat |= status;
spin_unlock_irqrestore(&priv->lock, flags);
schedule_work(&priv->bcmgenet_irq_work);
} }
return IRQ_HANDLED; return IRQ_HANDLED;
@ -3327,6 +3332,8 @@ static int bcmgenet_probe(struct platform_device *pdev)
goto err; goto err;
} }
spin_lock_init(&priv->lock);
SET_NETDEV_DEV(dev, &pdev->dev); SET_NETDEV_DEV(dev, &pdev->dev);
dev_set_drvdata(&pdev->dev, dev); dev_set_drvdata(&pdev->dev, dev);
ether_addr_copy(dev->dev_addr, macaddr); ether_addr_copy(dev->dev_addr, macaddr);

Просмотреть файл

@ -623,11 +623,13 @@ struct bcmgenet_priv {
struct work_struct bcmgenet_irq_work; struct work_struct bcmgenet_irq_work;
int irq0; int irq0;
int irq1; int irq1;
unsigned int irq0_stat;
unsigned int irq1_stat;
int wol_irq; int wol_irq;
bool wol_irq_disabled; bool wol_irq_disabled;
/* shared status */
spinlock_t lock;
unsigned int irq0_stat;
/* HW descriptors/checksum variables */ /* HW descriptors/checksum variables */
bool desc_64b_en; bool desc_64b_en;
bool desc_rxchk_en; bool desc_rxchk_en;