mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
net: macb: use context swapping in .set_ringparam()
ethtool_ops.set_ringparam() is implemented using the primitive close / update ring size / reopen sequence. Under memory pressure this does not fly: we free our buffers at close and cannot reallocate new ones at open. Also, it triggers a slow PHY reinit. Instead, exploit the new context mechanism and improve our sequence to: - allocate a new context (including buffers) first - if it fails, early return without any impact to the interface - stop interface - update global state (bp, netdev, etc) - pass buffer pointers to the hardware - start interface - free old context. The HW disable sequence is inspired by macb_reset_hw() but avoids (1) setting NCR bit CLRSTAT and (2) clearing register PBUFRXCUT. The HW re-enable sequence is inspired by macb_mac_link_up(), skipping over register writes which would be redundant (because values have not changed). The generic context swapping parts are isolated into helper functions macb_context_swap_start|end(), reusable by other operations (change_mtu, set_channels, etc). Introduce a new locking primitive (mac_cfg_lock mutex) to serialise swap with phylink MAC callbacks. Avoid stopping phylink to avoid a slow PHY retrain. We cannot sync to phylink ops using phydev->lock because it is not available in the SFP case. We cannot check link state using netif_carrier_ok() because we could race with its changes; so we use a redundant bp->link_up boolean that is mac_cfg_lock protected. AT91 EMAC is handled differently as their buffer management is separate and they don't do NAPI. They must never call swap_start/end(). Anyway they do not implement set_ringparam (-EOPNOTSUPP) so we are safe. Reviewed-by: Nicolai Buchwitz <nb@tipi-net.de> Signed-off-by: Théo Lebrun <theo.lebrun@bootlin.com> Link: https://patch.msgid.link/20261013-macb-context-v11-8-b27785167566@bootlin.com Tested-by: Nicolai Buchwitz <nb@tipi-net.de> # RPi CM5 with RP1 GEM Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
parent
88e99640fc
commit
dcc3288b94
|
|
@ -1358,6 +1358,8 @@ struct macb {
|
|||
struct macb_queue queues[MACB_MAX_QUEUES];
|
||||
|
||||
spinlock_t lock;
|
||||
/* Serializes context swap against phylink MAC callbacks. */
|
||||
struct mutex mac_cfg_lock;
|
||||
struct clk *pclk;
|
||||
struct clk *hclk;
|
||||
struct clk *tx_clk;
|
||||
|
|
@ -1419,10 +1421,13 @@ struct macb {
|
|||
u32 tx_lpi_timer;
|
||||
|
||||
/* ISR must not drive NAPI & BH mechanisms. True when the interface
|
||||
* is closed. Protected by bp->lock.
|
||||
* is closed or during context-swap. Protected by bp->lock.
|
||||
*/
|
||||
bool irq_quiesced;
|
||||
|
||||
/* Redundant to netif_carrier_ok(), but set under bp->mac_cfg_lock. */
|
||||
bool link_up;
|
||||
|
||||
u32 rx_intr_mask;
|
||||
|
||||
struct macb_pm_data pm_data;
|
||||
|
|
|
|||
|
|
@ -746,12 +746,26 @@ static void macb_mac_disable_tx_lpi(struct phylink_config *config)
|
|||
struct macb *bp = netdev_priv(netdev);
|
||||
unsigned long flags;
|
||||
|
||||
mutex_lock(&bp->mac_cfg_lock);
|
||||
|
||||
cancel_delayed_work_sync(&bp->tx_lpi_work);
|
||||
|
||||
spin_lock_irqsave(&bp->lock, flags);
|
||||
bp->eee_active = false;
|
||||
macb_tx_lpi_set(bp, false);
|
||||
spin_unlock_irqrestore(&bp->lock, flags);
|
||||
|
||||
mutex_unlock(&bp->mac_cfg_lock);
|
||||
}
|
||||
|
||||
static void macb_txp_lpi_initial_defer(struct macb *bp)
|
||||
{
|
||||
lockdep_assert_held(&bp->mac_cfg_lock);
|
||||
|
||||
/* Defer initial LPI entry by 1 second after link-up per
|
||||
* IEEE 802.3az section 22.7a.
|
||||
*/
|
||||
mod_delayed_work(system_wq, &bp->tx_lpi_work, msecs_to_jiffies(1000));
|
||||
}
|
||||
|
||||
static int macb_mac_enable_tx_lpi(struct phylink_config *config, u32 timer,
|
||||
|
|
@ -761,15 +775,16 @@ static int macb_mac_enable_tx_lpi(struct phylink_config *config, u32 timer,
|
|||
struct macb *bp = netdev_priv(netdev);
|
||||
unsigned long flags;
|
||||
|
||||
mutex_lock(&bp->mac_cfg_lock);
|
||||
|
||||
spin_lock_irqsave(&bp->lock, flags);
|
||||
bp->tx_lpi_timer = timer;
|
||||
bp->eee_active = true;
|
||||
spin_unlock_irqrestore(&bp->lock, flags);
|
||||
|
||||
/* Defer initial LPI entry by 1 second after link-up per
|
||||
* IEEE 802.3az section 22.7a.
|
||||
*/
|
||||
mod_delayed_work(system_wq, &bp->tx_lpi_work, msecs_to_jiffies(1000));
|
||||
macb_txp_lpi_initial_defer(bp);
|
||||
|
||||
mutex_unlock(&bp->mac_cfg_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -783,6 +798,7 @@ static void macb_mac_config(struct phylink_config *config, unsigned int mode,
|
|||
u32 old_ctrl, ctrl;
|
||||
u32 old_ncr, ncr;
|
||||
|
||||
mutex_lock(&bp->mac_cfg_lock);
|
||||
spin_lock_irqsave(&bp->lock, flags);
|
||||
|
||||
old_ctrl = ctrl = macb_or_gem_readl(bp, NCFGR);
|
||||
|
|
@ -816,6 +832,7 @@ static void macb_mac_config(struct phylink_config *config, unsigned int mode,
|
|||
macb_or_gem_writel(bp, NCR, ncr);
|
||||
|
||||
spin_unlock_irqrestore(&bp->lock, flags);
|
||||
mutex_unlock(&bp->mac_cfg_lock);
|
||||
}
|
||||
|
||||
static void macb_mac_link_down(struct phylink_config *config, unsigned int mode,
|
||||
|
|
@ -827,6 +844,10 @@ static void macb_mac_link_down(struct phylink_config *config, unsigned int mode,
|
|||
unsigned int q;
|
||||
u32 ctrl;
|
||||
|
||||
mutex_lock(&bp->mac_cfg_lock);
|
||||
|
||||
bp->link_up = false;
|
||||
|
||||
if (!(bp->caps & MACB_CAPS_MACB_IS_EMAC))
|
||||
for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue)
|
||||
queue_writel(queue, IDR,
|
||||
|
|
@ -837,6 +858,8 @@ static void macb_mac_link_down(struct phylink_config *config, unsigned int mode,
|
|||
macb_writel(bp, NCR, ctrl);
|
||||
|
||||
netif_tx_stop_all_queues(netdev);
|
||||
|
||||
mutex_unlock(&bp->mac_cfg_lock);
|
||||
}
|
||||
|
||||
/* Use juggling algorithm to left rotate tx ring and tx skb array */
|
||||
|
|
@ -945,8 +968,11 @@ static void macb_mac_link_up(struct phylink_config *config,
|
|||
unsigned int q;
|
||||
u32 ctrl;
|
||||
|
||||
mutex_lock(&bp->mac_cfg_lock);
|
||||
spin_lock_irqsave(&bp->lock, flags);
|
||||
|
||||
bp->link_up = true;
|
||||
|
||||
ctrl = macb_or_gem_readl(bp, NCFGR);
|
||||
|
||||
ctrl &= ~(MACB_BIT(SPD) | MACB_BIT(FD));
|
||||
|
|
@ -996,6 +1022,8 @@ static void macb_mac_link_up(struct phylink_config *config,
|
|||
macb_writel(bp, NCR, ctrl | MACB_BIT(RE) | MACB_BIT(TE));
|
||||
|
||||
netif_tx_wake_all_queues(netdev);
|
||||
|
||||
mutex_unlock(&bp->mac_cfg_lock);
|
||||
}
|
||||
|
||||
static struct phylink_pcs *macb_mac_select_pcs(struct phylink_config *config,
|
||||
|
|
@ -2010,29 +2038,37 @@ static int macb_rx_poll(struct napi_struct *napi, int budget)
|
|||
|
||||
static void macb_tx_restart(struct macb_queue *queue)
|
||||
{
|
||||
struct macb_context *ctx = queue->bp->ctx;
|
||||
struct macb_txq *txq = macb_txq(queue);
|
||||
struct macb *bp = queue->bp;
|
||||
unsigned int head_idx, tbqp;
|
||||
struct macb_context *ctx;
|
||||
struct macb_txq *txq;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&queue->tx_ptr_lock, flags);
|
||||
|
||||
spin_lock(&bp->lock);
|
||||
|
||||
/* context swap ongoing? */
|
||||
if (bp->irq_quiesced)
|
||||
goto out_unlock;
|
||||
|
||||
ctx = queue->bp->ctx;
|
||||
txq = macb_txq(queue);
|
||||
|
||||
if (txq->head == txq->tail)
|
||||
goto out_tx_ptr_unlock;
|
||||
goto out_unlock;
|
||||
|
||||
tbqp = queue_readl(queue, TBQP) / macb_dma_desc_get_size(ctx->info);
|
||||
tbqp = macb_adj_dma_desc_idx(ctx, macb_tx_ring_wrap(ctx, tbqp));
|
||||
head_idx = macb_adj_dma_desc_idx(ctx, macb_tx_ring_wrap(ctx, txq->head));
|
||||
|
||||
if (tbqp == head_idx)
|
||||
goto out_tx_ptr_unlock;
|
||||
goto out_unlock;
|
||||
|
||||
spin_lock(&bp->lock);
|
||||
macb_writel(bp, NCR, macb_readl(bp, NCR) | MACB_BIT(TSTART));
|
||||
spin_unlock(&bp->lock);
|
||||
|
||||
out_tx_ptr_unlock:
|
||||
out_unlock:
|
||||
spin_unlock(&bp->lock);
|
||||
spin_unlock_irqrestore(&queue->tx_ptr_lock, flags);
|
||||
}
|
||||
|
||||
|
|
@ -2291,7 +2327,9 @@ static irqreturn_t macb_interrupt(int irq, void *dev_id)
|
|||
spin_lock(&bp->lock);
|
||||
|
||||
while (status) {
|
||||
/* self-disarm while the netdev is closed */
|
||||
/* self-disarm while the netdev is closed
|
||||
* or during context swap
|
||||
*/
|
||||
if (unlikely(bp->irq_quiesced)) {
|
||||
queue_writel(queue, IDR, -1);
|
||||
macb_queue_isr_clear(bp, queue, -1);
|
||||
|
|
@ -3251,6 +3289,112 @@ static void macb_configure_dma(struct macb *bp)
|
|||
}
|
||||
}
|
||||
|
||||
static void macb_context_swap_start(struct macb *bp)
|
||||
{
|
||||
struct macb_queue *queue;
|
||||
unsigned long flags;
|
||||
unsigned int q;
|
||||
u32 ctrl;
|
||||
|
||||
mutex_lock(&bp->mac_cfg_lock);
|
||||
|
||||
macb_quiesce_start(bp);
|
||||
|
||||
/* Can finally disable software Tx; need to wait until napi_tx and
|
||||
* tx_error_task cannot be scheduled as either might wakeup Tx.
|
||||
*/
|
||||
netif_tx_disable(bp->netdev);
|
||||
|
||||
/* Now that everything is stopped, clear DQL. */
|
||||
for (q = 0; q < bp->num_queues; ++q)
|
||||
netdev_tx_reset_queue(netdev_get_tx_queue(bp->netdev, q));
|
||||
|
||||
/* Safe to call outside bp->lock because bp->irq_quiesced ensures the IRQ
|
||||
* handling is a no-op and all BH features are disabled.
|
||||
*
|
||||
* Whether it fails or not we'll disable TE/RE next.
|
||||
* We were just trying to be nice.
|
||||
*/
|
||||
macb_halt_tx(bp);
|
||||
|
||||
spin_lock_irqsave(&bp->lock, flags);
|
||||
|
||||
ctrl = macb_readl(bp, NCR);
|
||||
macb_writel(bp, NCR, ctrl & ~(MACB_BIT(RE) | MACB_BIT(TE)));
|
||||
|
||||
for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) {
|
||||
queue_writel(queue, IDR, -1);
|
||||
queue_readl(queue, ISR);
|
||||
macb_queue_isr_clear(bp, queue, -1);
|
||||
}
|
||||
|
||||
macb_writel(bp, TSR, -1);
|
||||
macb_writel(bp, RSR, -1);
|
||||
|
||||
spin_unlock_irqrestore(&bp->lock, flags);
|
||||
}
|
||||
|
||||
static void macb_context_swap_end(struct macb *bp,
|
||||
struct macb_context *new_ctx)
|
||||
{
|
||||
struct macb_context *old_ctx;
|
||||
struct macb_queue *queue;
|
||||
unsigned long flags;
|
||||
unsigned int q;
|
||||
u32 ctrl;
|
||||
|
||||
lockdep_assert_held(&bp->mac_cfg_lock);
|
||||
|
||||
/* Swap contexts & give buffer pointers to HW. */
|
||||
|
||||
old_ctx = bp->ctx;
|
||||
WRITE_ONCE(bp->ctx, new_ctx);
|
||||
wmb(); /* ensure IRQ enabled below read the new context */
|
||||
macb_init_buffers(bp);
|
||||
|
||||
/* Start NAPI, HW Tx/Rx and software Tx. */
|
||||
|
||||
for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) {
|
||||
napi_enable(&queue->napi_rx);
|
||||
napi_enable(&queue->napi_tx);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&bp->lock, flags);
|
||||
|
||||
/* Re-arm normal interrupt processing before enabling IRQs. */
|
||||
bp->irq_quiesced = false;
|
||||
|
||||
macb_configure_dma(bp);
|
||||
|
||||
if (bp->link_up) {
|
||||
for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) {
|
||||
queue_writel(queue, IER,
|
||||
bp->rx_intr_mask |
|
||||
MACB_TX_INT_FLAGS |
|
||||
MACB_BIT(HRESP));
|
||||
}
|
||||
|
||||
ctrl = macb_readl(bp, NCR);
|
||||
macb_writel(bp, NCR, ctrl | MACB_BIT(RE) | MACB_BIT(TE));
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&bp->lock, flags);
|
||||
|
||||
if (bp->link_up) {
|
||||
netif_tx_wake_all_queues(bp->netdev);
|
||||
|
||||
if (bp->eee_active)
|
||||
macb_txp_lpi_initial_defer(bp);
|
||||
}
|
||||
|
||||
mutex_unlock(&bp->mac_cfg_lock);
|
||||
|
||||
/* Free old context. */
|
||||
|
||||
macb_free(old_ctx);
|
||||
kfree(old_ctx);
|
||||
}
|
||||
|
||||
static void macb_init_hw(struct macb *bp)
|
||||
{
|
||||
u32 config;
|
||||
|
|
@ -3971,9 +4115,10 @@ static int macb_set_ringparam(struct net_device *netdev,
|
|||
struct kernel_ethtool_ringparam *kernel_ring,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
unsigned int new_rx_size, new_tx_size;
|
||||
struct macb *bp = netdev_priv(netdev);
|
||||
u32 new_rx_size, new_tx_size;
|
||||
unsigned int reset = 0;
|
||||
bool running = netif_running(netdev);
|
||||
struct macb_context *new_ctx;
|
||||
|
||||
if (bp->caps & MACB_CAPS_MACB_IS_EMAC)
|
||||
return -EOPNOTSUPP;
|
||||
|
|
@ -3995,16 +4140,20 @@ static int macb_set_ringparam(struct net_device *netdev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (netif_running(bp->netdev)) {
|
||||
reset = 1;
|
||||
macb_close(bp->netdev);
|
||||
if (running) {
|
||||
new_ctx = macb_context_alloc(bp, netdev->mtu,
|
||||
new_rx_size, new_tx_size);
|
||||
if (IS_ERR(new_ctx))
|
||||
return PTR_ERR(new_ctx);
|
||||
|
||||
macb_context_swap_start(bp);
|
||||
}
|
||||
|
||||
bp->configured_rx_ring_size = new_rx_size;
|
||||
bp->configured_tx_ring_size = new_tx_size;
|
||||
|
||||
if (reset)
|
||||
macb_open(bp->netdev);
|
||||
if (running)
|
||||
macb_context_swap_end(bp, new_ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -6185,6 +6334,7 @@ static int macb_probe(struct platform_device *pdev)
|
|||
spin_lock_init(&bp->lock);
|
||||
spin_lock_init(&bp->stats_lock);
|
||||
spin_lock_init(&bp->tsu_clk_lock);
|
||||
mutex_init(&bp->mac_cfg_lock);
|
||||
|
||||
/* setup capabilities */
|
||||
macb_configure_caps(bp, macb_config);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user