Merge branch 'net-macb-implement-context-swapping'

says:

====================
net: macb: implement context swapping

MACB has a pretty primitive approach to buffer management. They are all
stored in `struct macb *bp`. On operations that require buffer realloc
(set_ringparam & change_mtu at the moment), the only option is to close
the interface, change our global state and re-open the interface.

Two issues:
- It doesn't fly on memory pressured systems; we free our precious
  buffers and don't manage to reallocate fully, meaning our machine
  just lost its network access.
- Anecdotally, it is pretty slow because it implies a full PHY reinit.

Instead, we shall:
 - 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 newly allocated buffer pointers to the hardware
 - start interface
 - free old context

This is what we implement here. Both .set_ringparam() and
.ndo_change_mtu() are covered by this series. In the future,
at least .set_channels() [0], XDP [1] and XSK [2] would benefit.
Tested-by: Nicolai Buchwitz <nb@tipi-net.de> # RPi CM5 with RP1 GEM
---
I wonder if we can get those changes in before the next merge window?
The idea being that we can restart iterating on XDP [1] & XSK [2]
during the next kernel cycle, bringing those long awaited features into
MACB.

[0]: https://lore.kernel.org/netdev/20260317-macb-set-channels-v4-0-1bd4f4ffcfca@bootlin.com/
[1]: https://lore.kernel.org/netdev/20260323221047.2749577-1-pvalerio@redhat.com/
[2]: https://lore.kernel.org/netdev/20260304-macb-xsk-v1-0-ba2ebe2bdaa3@bootlin.com/
[3]: https://lore.kernel.org/all/DJXGIM9EGPT8.4UHNYP2Y0GOP@bootlin.com/
[4]: https://lore.kernel.org/netdev/20260930-macb-irq-v1-1-8994a4c8f3bb@bootlin.com/
[5]: https://lore.kernel.org/netdev/20260918-macb-ncr-rmw-v1-1-30c3494f93cd@bootlin.com/

- v10: https://patch.msgid.link/20261001-macb-context-v10-0-beb541bbb7df@bootlin.com
- v9: https://patch.msgid.link/20260812-macb-context-v9-0-7ddbf5f715e0@bootlin.com
- v8: https://patch.msgid.link/20260805-macb-context-v8-0-bc302ffd1174@bootlin.com
- v7: https://patch.msgid.link/20260803-macb-context-v7-0-4d7d4af04849@bootlin.com
- v6: https://patch.msgid.link/20260731-macb-context-v6-0-49d5a1439d48@bootlin.com
- v5: https://patch.msgid.link/20260724-macb-context-v5-0-569b1852bc7f@bootlin.com
- v4: https://patch.msgid.link/20260717-macb-context-v4-0-0acbe7f10cdb@bootlin.com
- v3: https://patch.msgid.link/20260701-macb-context-v3-0-00268d5b1502@bootlin.com
- v2: https://patch.msgid.link/20260410-macb-context-v2-0-af39f71d40b6@bootlin.com
- v1: https://patch.msgid.link/20260401-macb-context-v1-0-9590c5ab7272@bootlin.com
====================

Link: https://patch.msgid.link/20261013-macb-context-v11-0-b27785167566@bootlin.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni 2026-10-20 09:30:55 +02:00
commit f34e7e8875
3 changed files with 824 additions and 441 deletions

View File

@ -834,7 +834,7 @@
*/
#define macb_or_gem_writel(__bp, __reg, __value) \
({ \
if (macb_is_gem((__bp))) \
if (macb_is_gem(&(__bp)->info)) \
gem_writel((__bp), __reg, __value); \
else \
macb_writel((__bp), __reg, __value); \
@ -843,7 +843,7 @@
#define macb_or_gem_readl(__bp, __reg) \
({ \
u32 __v; \
if (macb_is_gem((__bp))) \
if (macb_is_gem(&(__bp)->info)) \
__v = gem_readl((__bp), __reg); \
else \
__v = macb_readl((__bp), __reg); \
@ -1193,11 +1193,12 @@ static const struct gem_statistic queue_statistics[] = {
struct macb;
struct macb_queue;
struct macb_context;
struct macb_or_gem_ops {
int (*mog_alloc_rx_buffers)(struct macb *bp);
void (*mog_free_rx_buffers)(struct macb *bp);
int (*mog_init_rings)(struct macb *bp);
int (*mog_alloc_rx_buffers)(struct macb_context *ctx);
void (*mog_free_rx_buffers)(struct macb_context *ctx);
int (*mog_init_rings)(struct macb_context *ctx);
int (*mog_rx)(struct macb_queue *queue, struct napi_struct *napi,
int budget);
};
@ -1267,23 +1268,12 @@ struct macb_queue {
unsigned int ENST_ON_TIME;
unsigned int ENST_OFF_TIME;
/* Lock to protect tx_head and tx_tail */
/* Lock to protect ctx->txq[q].head and ctx->txq[q].tail */
spinlock_t tx_ptr_lock;
unsigned int tx_head, tx_tail;
struct macb_dma_desc *tx_ring;
struct macb_tx_skb *tx_skb;
dma_addr_t tx_ring_dma;
struct work_struct tx_error_task;
bool txubr_pending;
struct napi_struct napi_tx;
dma_addr_t rx_ring_dma;
dma_addr_t rx_buffers_dma;
unsigned int rx_tail;
unsigned int rx_prepared_head;
struct macb_dma_desc *rx_ring;
struct sk_buff **rx_skbuff;
void *rx_buffers;
struct napi_struct napi_rx;
struct queue_stats stats;
};
@ -1298,6 +1288,45 @@ struct ethtool_rx_fs_list {
unsigned int count;
};
struct macb_info {
struct platform_device *pdev;
struct net_device *netdev;
struct macb_or_gem_ops macbgem_ops;
unsigned int num_queues;
u32 caps;
int rx_bd_rd_prefetch;
int tx_bd_rd_prefetch;
struct queue_stats *queue_stats[MACB_MAX_QUEUES];
};
struct macb_rxq {
struct macb_dma_desc *ring; /* MACB & GEM */
dma_addr_t ring_dma; /* MACB & GEM */
unsigned int tail; /* MACB & GEM */
unsigned int prepared_head; /* GEM */
struct sk_buff **skbuff; /* GEM */
dma_addr_t buffers_dma; /* MACB */
void *buffers; /* MACB */
};
struct macb_txq {
unsigned int head;
unsigned int tail;
struct macb_dma_desc *ring;
dma_addr_t ring_dma;
struct macb_tx_skb *skb;
};
struct macb_context {
const struct macb_info *info;
unsigned int rx_buffer_size;
unsigned int rx_ring_size;
unsigned int tx_ring_size;
struct macb_rxq rxq[MACB_MAX_QUEUES];
struct macb_txq txq[MACB_MAX_QUEUES];
};
struct macb {
void __iomem *regs;
bool native_io;
@ -1306,24 +1335,36 @@ struct macb {
u32 (*macb_reg_readl)(struct macb *bp, int offset);
void (*macb_reg_writel)(struct macb *bp, int offset, u32 value);
/*
* Give direct access (bp->caps) and
* allow taking a pointer to it (&bp->info) for contexts.
*/
union {
struct macb_info;
struct macb_info info;
};
/*
* Context stores all its parameters.
* But we must remember them across closure.
*/
unsigned int configured_rx_ring_size;
unsigned int configured_tx_ring_size;
struct macb_context *ctx;
struct macb_dma_desc *rx_ring_tieoff;
dma_addr_t rx_ring_tieoff_dma;
size_t rx_buffer_size;
unsigned int rx_ring_size;
unsigned int tx_ring_size;
unsigned int num_queues;
struct macb_queue queues[MACB_MAX_QUEUES];
spinlock_t lock;
struct platform_device *pdev;
/* Serializes context swap against phylink MAC callbacks. */
struct mutex mac_cfg_lock;
struct clk *pclk;
struct clk *hclk;
struct clk *tx_clk;
struct clk *rx_clk;
struct clk *tsu_clk;
struct net_device *netdev;
/* Protects hw_stats and ethtool_stats */
spinlock_t stats_lock;
union {
@ -1331,15 +1372,12 @@ struct macb {
struct gem_stats gem;
} hw_stats;
struct macb_or_gem_ops macbgem_ops;
struct mii_bus *mii_bus;
struct phylink *phylink;
struct phylink_config phylink_config;
struct phylink_pcs phylink_usx_pcs;
struct phylink_pcs phylink_sgmii_pcs;
u32 caps;
unsigned int dma_burst_length;
phy_interface_t phy_interface;
@ -1383,12 +1421,12 @@ 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;
int rx_bd_rd_prefetch;
int tx_bd_rd_prefetch;
/* Redundant to netif_carrier_ok(), but set under bp->mac_cfg_lock. */
bool link_up;
u32 rx_intr_mask;
@ -1441,14 +1479,15 @@ static inline void gem_ptp_do_txstamp(struct macb *bp, struct sk_buff *skb, stru
static inline void gem_ptp_do_rxstamp(struct macb *bp, struct sk_buff *skb, struct macb_dma_desc *desc) { }
#endif
static inline bool macb_is_gem(struct macb *bp)
static inline bool macb_is_gem(const struct macb_info *info)
{
return !!(bp->caps & MACB_CAPS_MACB_IS_GEM);
return !!(info->caps & MACB_CAPS_MACB_IS_GEM);
}
static inline bool gem_has_ptp(struct macb *bp)
static inline bool gem_has_ptp(const struct macb_info *info)
{
return IS_ENABLED(CONFIG_MACB_USE_HWSTAMP) && (bp->caps & MACB_CAPS_GEM_HAS_PTP);
return IS_ENABLED(CONFIG_MACB_USE_HWSTAMP) &&
(info->caps & MACB_CAPS_GEM_HAS_PTP);
}
/* ENST Helper functions */
@ -1464,16 +1503,16 @@ static inline u64 enst_max_hw_interval(u32 speed_mbps)
ENST_TIME_GRANULARITY_NS * 1000, (speed_mbps));
}
static inline bool macb_dma64(struct macb *bp)
static inline bool macb_dma64(const struct macb_info *info)
{
return IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT) &&
bp->caps & MACB_CAPS_DMA_64B;
info->caps & MACB_CAPS_DMA_64B;
}
static inline bool macb_dma_ptp(struct macb *bp)
static inline bool macb_dma_ptp(const struct macb_info *info)
{
return IS_ENABLED(CONFIG_MACB_USE_HWSTAMP) &&
bp->caps & MACB_CAPS_DMA_PTP;
info->caps & MACB_CAPS_DMA_PTP;
}
static inline void macb_queue_isr_clear(struct macb *bp,

File diff suppressed because it is too large Load Diff

View File

@ -28,10 +28,10 @@
static struct macb_dma_desc_ptp *macb_ptp_desc(struct macb *bp,
struct macb_dma_desc *desc)
{
if (!macb_dma_ptp(bp))
if (!macb_dma_ptp(&bp->info))
return NULL;
if (macb_dma64(bp))
if (macb_dma64(&bp->info))
return (struct macb_dma_desc_ptp *)
((u8 *)desc + sizeof(struct macb_dma_desc)
+ sizeof(struct macb_dma_desc_64));
@ -380,7 +380,7 @@ int gem_get_hwtst(struct net_device *netdev,
struct macb *bp = netdev_priv(netdev);
*tstamp_config = bp->tstamp_config;
if (!macb_dma_ptp(bp))
if (!macb_dma_ptp(&bp->info))
return -EOPNOTSUPP;
return 0;
@ -399,7 +399,7 @@ int gem_set_hwtst(struct net_device *netdev,
u32 ncr_bits = 0;
u32 regval;
if (!macb_dma_ptp(bp))
if (!macb_dma_ptp(&bp->info))
return -EOPNOTSUPP;
switch (tstamp_config->tx_type) {