Skip to content

Commit

Permalink
net: renesas: rswitch: handle stop vs interrupt race
Browse files Browse the repository at this point in the history
Currently the stop routine of rswitch driver does not immediately
prevent hardware from continuing to update descriptors and requesting
interrupts.

It can happen that when rswitch_stop() executes the masking of
interrupts from the queues of the port being closed, napi poll for
that port is already scheduled or running on a different CPU. When
execution of this napi poll completes, it will unmask the interrupts.
And unmasked interrupt can fire after rswitch_stop() returns from
napi_disable() call. Then, the handler won't mask it, because
napi_schedule_prep() will return false, and interrupt storm will
happen.

This can't be fixed by making rswitch_stop() call napi_disable() before
masking interrupts. In this case, the interrupt storm will happen if
interrupt fires between napi_disable() and masking.

Fix this by checking for priv->opened_ports bit when unmasking
interrupts after napi poll. For that to be consistent, move
priv->opened_ports changes into spinlock-protected areas, and reorder
other operations in rswitch_open() and rswitch_stop() accordingly.

Signed-off-by: Nikita Yushchenko <nikita.yoush@cogentembedded.com>
Reviewed-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Fixes: 3590918 ("net: ethernet: renesas: Add support for "Ethernet Switch"")
Link: https://patch.msgid.link/20241209113204.175015-1-nikita.yoush@cogentembedded.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
  • Loading branch information
nikita-yoush authored and kuba-moo committed Dec 11, 2024
1 parent 93763e6 commit 3dd002f
Showing 1 changed file with 18 additions and 15 deletions.
33 changes: 18 additions & 15 deletions drivers/net/ethernet/renesas/rswitch.c
Original file line number Diff line number Diff line change
Expand Up @@ -908,8 +908,10 @@ static int rswitch_poll(struct napi_struct *napi, int budget)

if (napi_complete_done(napi, budget - quota)) {
spin_lock_irqsave(&priv->lock, flags);
rswitch_enadis_data_irq(priv, rdev->tx_queue->index, true);
rswitch_enadis_data_irq(priv, rdev->rx_queue->index, true);
if (test_bit(rdev->port, priv->opened_ports)) {
rswitch_enadis_data_irq(priv, rdev->tx_queue->index, true);
rswitch_enadis_data_irq(priv, rdev->rx_queue->index, true);
}
spin_unlock_irqrestore(&priv->lock, flags);
}

Expand Down Expand Up @@ -1538,20 +1540,20 @@ static int rswitch_open(struct net_device *ndev)
struct rswitch_device *rdev = netdev_priv(ndev);
unsigned long flags;

phy_start(ndev->phydev);
if (bitmap_empty(rdev->priv->opened_ports, RSWITCH_NUM_PORTS))
iowrite32(GWCA_TS_IRQ_BIT, rdev->priv->addr + GWTSDIE);

napi_enable(&rdev->napi);
netif_start_queue(ndev);

spin_lock_irqsave(&rdev->priv->lock, flags);
bitmap_set(rdev->priv->opened_ports, rdev->port, 1);
rswitch_enadis_data_irq(rdev->priv, rdev->tx_queue->index, true);
rswitch_enadis_data_irq(rdev->priv, rdev->rx_queue->index, true);
spin_unlock_irqrestore(&rdev->priv->lock, flags);

if (bitmap_empty(rdev->priv->opened_ports, RSWITCH_NUM_PORTS))
iowrite32(GWCA_TS_IRQ_BIT, rdev->priv->addr + GWTSDIE);
phy_start(ndev->phydev);

bitmap_set(rdev->priv->opened_ports, rdev->port, 1);
netif_start_queue(ndev);

return 0;
};
Expand All @@ -1563,7 +1565,16 @@ static int rswitch_stop(struct net_device *ndev)
unsigned long flags;

netif_tx_stop_all_queues(ndev);

phy_stop(ndev->phydev);

spin_lock_irqsave(&rdev->priv->lock, flags);
rswitch_enadis_data_irq(rdev->priv, rdev->tx_queue->index, false);
rswitch_enadis_data_irq(rdev->priv, rdev->rx_queue->index, false);
bitmap_clear(rdev->priv->opened_ports, rdev->port, 1);
spin_unlock_irqrestore(&rdev->priv->lock, flags);

napi_disable(&rdev->napi);

if (bitmap_empty(rdev->priv->opened_ports, RSWITCH_NUM_PORTS))
iowrite32(GWCA_TS_IRQ_BIT, rdev->priv->addr + GWTSDID);
Expand All @@ -1576,14 +1587,6 @@ static int rswitch_stop(struct net_device *ndev)
kfree(ts_info);
}

spin_lock_irqsave(&rdev->priv->lock, flags);
rswitch_enadis_data_irq(rdev->priv, rdev->tx_queue->index, false);
rswitch_enadis_data_irq(rdev->priv, rdev->rx_queue->index, false);
spin_unlock_irqrestore(&rdev->priv->lock, flags);

phy_stop(ndev->phydev);
napi_disable(&rdev->napi);

return 0;
};

Expand Down

0 comments on commit 3dd002f

Please sign in to comment.