* [PATCH net-next v3 1/2] net: ethernet: mtk_eth_soc: skip page_pool
2026-11-08 20:38 [PATCH net-next v3 0/2] net: ethernet: mtk_eth_soc: 9K jumbo frame support Sandeep Haemoon
@ 2026-11-08 20:37 ` Sandeep Haemoon
2026-11-09 22:19 ` netdev-bot+sashiko
2026-11-08 20:38 ` [PATCH net-next v3 2/2] net: ethernet: mtk_eth_soc: add 9K jumbo Sandeep Haemoon
1 sibling, 1 reply; 5+ messages in thread
From: Sandeep Haemoon @ 2026-11-08 20:37 UTC (permalink / raw)
To: netdev
Cc: Felix Fietkau, Lorenzo Bianconi, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Matthias Brugger,
AngeloGioacchino Del Regno, Russell King, Mason Chang,
Frank Wunderlich, linux-mediatek, linux-arm-kernel, linux-kernel
page_pool hands out order-0 pages and the pool is created with
MTK_PP_MAX_BUF_SIZE as the maximum DMA length, so it can only back a ring
whose descriptor length (ring->buf_size) fits in that.
Gate the pool on ring->buf_size instead of ring->frag_size: frag_size is
computed with the netdev_alloc_frag() layout, whose headroom and
skb_shared_info footprint differ from the pool's, so a ring can pass a
frag_size <= PAGE_SIZE test while its descriptor length is already larger
than the pool can map.
This is preparation for the 9K support in the next patch. Today every
ring is smaller than the pool's limit and the new condition is always
true, so there is no functional change; once the normal ring follows a
jumbo MTU it grows past the limit and takes the netdev_alloc_frag()/
mtk_max_lro_buf_alloc() path that is already there.
Link: https://github.com/mediatek/mtk-openwrt-feeds
Reviewed-by: Simon Horman <horms@kernel.org>
Signed-off-by: Sandeep Haemoon <sandeeph@loljews.com>
---
drivers/net/ethernet/mediatek/mtk_eth_soc.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index ad0fdc413..f2b2fb113 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -2819,7 +2819,15 @@ static int mtk_rx_alloc(struct mtk_eth *eth, int ring_no, int rx_flag)
if (!ring->data)
return -ENOMEM;
- if (mtk_page_pool_enabled(eth)) {
+ /* page_pool hands out order-0 pages and caps the DMA mapping at
+ * MTK_PP_MAX_BUF_SIZE, so it can only back a ring whose descriptor
+ * length fits that. Today every ring does; this is the preparation
+ * for the 9K support in the next patch, where the normal ring grows
+ * past the pool's limit and takes the netdev_alloc_frag()/
+ * mtk_max_lro_buf_alloc() path below instead.
+ */
+ if (mtk_page_pool_enabled(eth) &&
+ ring->buf_size <= MTK_PP_MAX_BUF_SIZE) {
struct page_pool *pp;
pp = mtk_create_page_pool(eth, &ring->xdp_q, ring_no,
--
2.53.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH net-next v3 2/2] net: ethernet: mtk_eth_soc: add 9K jumbo
2026-11-08 20:38 [PATCH net-next v3 0/2] net: ethernet: mtk_eth_soc: 9K jumbo frame support Sandeep Haemoon
2026-11-08 20:37 ` [PATCH net-next v3 1/2] net: ethernet: mtk_eth_soc: skip page_pool Sandeep Haemoon
@ 2026-11-08 20:38 ` Sandeep Haemoon
2026-11-09 22:19 ` netdev-bot+sashiko
1 sibling, 1 reply; 5+ messages in thread
From: Sandeep Haemoon @ 2026-11-08 20:38 UTC (permalink / raw)
To: netdev
Cc: Felix Fietkau, Lorenzo Bianconi, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Matthias Brugger,
AngeloGioacchino Del Regno, Russell King, Mason Chang,
Frank Wunderlich, linux-mediatek, linux-arm-kernel, linux-kernel
The driver caps max_mtu at 2030 bytes and programs the MAC max-rx field
for at most 2048-byte frames, so MTU > 2K is rejected and 9K jumbo
frames are not usable on the 10G XGMII MACs.
Add a MTK_NETSYS_RX_9K capability for MT7988 and:
- advertise 9K max_mtu on the XGMII MACs, from the DT phy_mode at probe
time, so DSA can raise the conduit MTU before the netdev is opened;
- program XMAC_RX_CFG2 on the XGMII MACs, keeping the bits outside the
length field;
- program the PDMA RX SDL max frame from the shared buffer length,
replacing the field instead of OR-ing into the value already there;
- derive the shared RX buffer length from the largest MTU of any MAC and
size the RX rings from it, with 2K as the minimum so the MTUs every SoC
accepts today keep working, at runtime included;
- program the MAC max-rx limits from that shared length in
mtk_mac_config(), so no MAC accepts a frame the rings cannot hold, not
even after a reopen with a smaller MTU;
- reject only a runtime MTU change that would need larger buffers while
DMA is running (-EBUSY). Shrinking is allowed: the rings keep the
buffers they already have.
XDP needs a page_pool, and the pool cannot back buffers larger than
MTK_PP_MAX_BUF_SIZE, so an MTU that needs a larger buffer cannot be
combined with XDP. Both the XDP attach and the MTU change are rejected
in that case, instead of letting a program attach and never run.
The RX rings are shared, so the largest MTU of any MAC pins order-2
buffers for every ring while DMA is up, around 64 MiB at 9K; the refill
in mtk_poll_rx() is then a high-order GFP_ATOMIC allocation. That is the
cost of the downstream design and is documented in the commit message on
purpose.
This is a forward-port of the MediaTek downstream jumbo work: Mason
Chang's "change default rx buffer length" and "add 9k jumbo frame
support" (MediaTek OpenWrt feed 999-eth-30/31, carried in
frank-w/BPI-Router-Linux), plus Frank Wunderlich's "try to fix crash the
right way". The runtime reallocation of Mason's "dynamic rx buffer
adjustment" (feed 999-eth-33) is not included; it will be sent
separately.
Link: https://github.com/frank-w/BPI-Router-Linux/commit/b4c6cb1d5532
Link: https://github.com/mediatek/mtk-openwrt-feeds
Signed-off-by: Sandeep Haemoon <sandeeph@loljews.com>
---
drivers/net/ethernet/mediatek/mtk_eth_soc.c | 205 +++++++++++++++-----
drivers/net/ethernet/mediatek/mtk_eth_soc.h | 10 +-
2 files changed, 164 insertions(+), 51 deletions(-)
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index f2b2fb113..c7bc8eaea 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -171,6 +171,7 @@ static const struct mtk_reg_map mt7988_reg_map = {
.glo_cfg = 0x6a04,
.rst_idx = 0x6a08,
.delay_irq = 0x6a0c,
+ .rx_cfg = 0x6a10,
.irq_status = 0x6a20,
.irq_mask = 0x6a28,
.adma_rx_dbg0 = 0x6a38,
@@ -554,6 +555,41 @@ static int mtk_mac_prepare(struct phylink_config *config, unsigned int mode,
return 0;
}
+static void mtk_set_mcr_max_rx(struct mtk_mac *mac, u32 val)
+{
+ struct mtk_eth *eth = mac->hw;
+ u32 mcr_cur, mcr_new;
+
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628))
+ return;
+
+ if (!mtk_interface_mode_is_xgmii(eth, mac->interface)) {
+ mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
+ mcr_new = mcr_cur & ~MAC_MCR_MAX_RX_MASK;
+
+ if (val <= 1518)
+ mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1518);
+ else if (val <= 1536)
+ mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1536);
+ else if (val <= 1552)
+ mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1552);
+ else
+ mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_2048);
+
+ if (mcr_new != mcr_cur)
+ mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id));
+ } else if (mtk_is_netsys_v3_or_greater(mac->hw) &&
+ mac->id != MTK_GMAC1_ID) {
+ mcr_cur = mtk_r32(mac->hw, MTK_XMAC_RX_CFG2(mac->id));
+ mcr_new = mcr_cur & ~MTK_XMAC_MAX_RX_MASK;
+ mcr_new |= FIELD_PREP(MTK_XMAC_MAX_RX_MASK,
+ min_t(u32, val, MTK_MAX_RX_LENGTH_9K));
+
+ if (mcr_new != mcr_cur)
+ mtk_w32(mac->hw, mcr_new, MTK_XMAC_RX_CFG2(mac->id));
+ }
+}
+
static void mtk_mac_config(struct phylink_config *config, unsigned int mode,
const struct phylink_link_state *state)
{
@@ -649,6 +685,13 @@ static void mtk_mac_config(struct phylink_config *config, unsigned int mode,
mac->interface = state->interface;
}
+ /* What a MAC may accept is a property of the shared RX rings, so
+ * rewrite the limit on every mac_config and not only when the
+ * interface mode changes: a reopen after an MTU change must narrow
+ * the MACs that still carry the previous, larger limit.
+ */
+ mtk_set_mcr_max_rx(mac, eth->rx_buf_len);
+
/* SGMII */
if (state->interface == PHY_INTERFACE_MODE_SGMII ||
phy_interface_mode_is_8023z(state->interface)) {
@@ -1242,22 +1285,22 @@ static void mtk_get_stats64(struct net_device *dev,
storage->tx_dropped = dev->stats.tx_dropped;
}
-static inline int mtk_max_frag_size(int mtu)
+static inline int mtk_max_frag_size(struct mtk_eth *eth, int mtu)
{
- /* make sure buf_size will be at least MTK_MAX_RX_LENGTH */
- if (mtu + MTK_RX_ETH_HLEN < MTK_MAX_RX_LENGTH_2K)
- mtu = MTK_MAX_RX_LENGTH_2K - MTK_RX_ETH_HLEN;
+ /* make sure buf_size will be at least max rx buffer length */
+ if (mtu + MTK_RX_ETH_HLEN < eth->rx_buf_len)
+ mtu = eth->rx_buf_len - MTK_RX_ETH_HLEN;
return SKB_DATA_ALIGN(MTK_RX_HLEN + mtu) +
SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
}
-static inline int mtk_max_buf_size(int frag_size)
+static inline int mtk_max_buf_size(struct mtk_eth *eth, int frag_size)
{
int buf_size = frag_size - NET_SKB_PAD - NET_IP_ALIGN -
SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
- WARN_ON(buf_size < MTK_MAX_RX_LENGTH_2K);
+ WARN_ON(buf_size < eth->rx_buf_len);
return buf_size;
}
@@ -1280,9 +1323,8 @@ static bool mtk_rx_get_desc(struct mtk_eth *eth, struct mtk_rx_dma_v2 *rxd,
return true;
}
-static void *mtk_max_lro_buf_alloc(gfp_t gfp_mask)
+static void *mtk_max_buf_alloc(unsigned int size, gfp_t gfp_mask)
{
- unsigned int size = mtk_max_frag_size(MTK_MAX_LRO_RX_LENGTH);
unsigned long data;
data = __get_free_pages(gfp_mask | __GFP_COMP | __GFP_NOWARN,
@@ -2310,7 +2352,8 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget,
if (ring->frag_size <= PAGE_SIZE)
new_data = napi_alloc_frag(ring->frag_size);
else
- new_data = mtk_max_lro_buf_alloc(GFP_ATOMIC);
+ new_data = mtk_max_buf_alloc(ring->frag_size,
+ GFP_ATOMIC);
if (unlikely(!new_data)) {
netdev->stats.rx_dropped++;
@@ -2812,8 +2855,8 @@ static int mtk_rx_alloc(struct mtk_eth *eth, int ring_no, int rx_flag)
rx_dma_size = soc->rx.dma_size;
}
- ring->frag_size = mtk_max_frag_size(rx_data_len);
- ring->buf_size = mtk_max_buf_size(ring->frag_size);
+ ring->frag_size = mtk_max_frag_size(eth, rx_data_len);
+ ring->buf_size = mtk_max_buf_size(eth, ring->frag_size);
ring->data = kcalloc(rx_dma_size, sizeof(*ring->data),
GFP_KERNEL);
if (!ring->data)
@@ -2860,7 +2903,8 @@ static int mtk_rx_alloc(struct mtk_eth *eth, int ring_no, int rx_flag)
if (ring->frag_size <= PAGE_SIZE)
data = netdev_alloc_frag(ring->frag_size);
else
- data = mtk_max_lro_buf_alloc(GFP_KERNEL);
+ data = mtk_max_buf_alloc(ring->frag_size,
+ GFP_KERNEL);
if (!data)
return -ENOMEM;
@@ -3256,6 +3300,55 @@ static int mtk_dma_busy_wait(struct mtk_eth *eth)
return ret;
}
+static int mtk_max_gmac_mtu(struct mtk_eth *eth)
+{
+ int i, max_mtu = ETH_DATA_LEN;
+
+ for (i = 0; i < ARRAY_SIZE(eth->netdev); i++)
+ if (eth->netdev[i] && eth->netdev[i]->mtu > max_mtu)
+ max_mtu = eth->netdev[i]->mtu;
+
+ return max_mtu;
+}
+
+static u32 mtk_rx_buf_len_from_mtu(int max_mtu)
+{
+ int length = max_mtu + MTK_RX_ETH_HLEN;
+
+ if (length <= MTK_MAX_RX_LENGTH_2K)
+ return MTK_MAX_RX_LENGTH_2K;
+
+ return DIV_ROUND_UP(length, 1024) * 1024;
+}
+
+/* The rx rings are shared by every MAC, so their buffers are sized for the
+ * largest MTU configured on any of them.
+ */
+static u32 mtk_rx_buf_len(struct mtk_eth *eth)
+{
+ return mtk_rx_buf_len_from_mtu(mtk_max_gmac_mtu(eth));
+}
+
+/* Program the PDMA RX max-frame field. The frame engine is only reset at
+ * probe and on error recovery, not on every open, so the field has to be
+ * replaced rather than OR-ed: a later open with a different MTU would
+ * otherwise accumulate the old value.
+ */
+static void mtk_pdma_set_sdl(struct mtk_eth *eth)
+{
+ const struct mtk_reg_map *reg_map = eth->soc->reg_map;
+ u32 val;
+
+ if (!mtk_is_netsys_v3_or_greater(eth))
+ return;
+
+ val = mtk_r32(eth, reg_map->pdma.rx_cfg);
+ val &= ~MTK_RX_CFG_SDL_MASK;
+ val |= FIELD_PREP(MTK_RX_CFG_SDL_MASK,
+ MTK_PDMA_LRO_SDL + eth->rx_buf_len);
+ mtk_w32(eth, val, reg_map->pdma.rx_cfg);
+}
+
static int mtk_dma_init(struct mtk_eth *eth)
{
int err;
@@ -3264,6 +3357,12 @@ static int mtk_dma_init(struct mtk_eth *eth)
if (mtk_dma_busy_wait(eth))
return -EBUSY;
+ /* Derived here, at ring allocation, so eth->rx_buf_len always describes
+ * the buffers the rings actually hold and mtk_mac_config() never lets
+ * a MAC accept a frame the rings cannot take.
+ */
+ eth->rx_buf_len = mtk_rx_buf_len(eth);
+
if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
/* QDMA needs scratch memory for internal reordering of the
* descriptors
@@ -3307,6 +3406,11 @@ static int mtk_dma_init(struct mtk_eth *eth)
mtk_w32(eth, 0x0, eth->soc->reg_map->qdma.hred);
}
+ /* Program the PDMA max RX frame (SDL) from rx_buf_len so jumbo
+ * frames up to it are accepted by the DMA engine.
+ */
+ mtk_pdma_set_sdl(eth);
+
return 0;
}
@@ -3597,17 +3701,6 @@ static int mtk_device_event(struct notifier_block *n, unsigned long event, void
return NOTIFY_DONE;
}
-static int mtk_max_gmac_mtu(struct mtk_eth *eth)
-{
- int i, max_mtu = ETH_DATA_LEN;
-
- for (i = 0; i < ARRAY_SIZE(eth->netdev); i++)
- if (eth->netdev[i] && eth->netdev[i]->mtu > max_mtu)
- max_mtu = eth->netdev[i]->mtu;
-
- return max_mtu;
-}
-
static int mtk_open(struct net_device *dev)
{
struct mtk_mac *mac = netdev_priv(dev);
@@ -3784,7 +3877,8 @@ static int mtk_xdp_setup(struct net_device *dev, struct bpf_prog *prog,
return -EOPNOTSUPP;
}
- if (dev->mtu > MTK_PP_MAX_BUF_SIZE) {
+ if (dev->mtu > MTK_PP_MAX_BUF_SIZE ||
+ mtk_rx_buf_len(eth) > MTK_PP_MAX_BUF_SIZE) {
NL_SET_ERR_MSG_MOD(extack, "MTU too large for XDP");
return -EOPNOTSUPP;
}
@@ -3924,29 +4018,6 @@ static void mtk_dim_tx(struct work_struct *work)
dim->state = DIM_START_MEASURE;
}
-static void mtk_set_mcr_max_rx(struct mtk_mac *mac, u32 val)
-{
- struct mtk_eth *eth = mac->hw;
- u32 mcr_cur, mcr_new;
-
- if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628))
- return;
-
- mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
- mcr_new = mcr_cur & ~MAC_MCR_MAX_RX_MASK;
-
- if (val <= 1518)
- mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1518);
- else if (val <= 1536)
- mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1536);
- else if (val <= 1552)
- mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1552);
- else
- mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_2048);
-
- if (mcr_new != mcr_cur)
- mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id));
-}
static void mtk_hw_reset(struct mtk_eth *eth)
{
@@ -4215,8 +4286,7 @@ static int mtk_hw_init(struct mtk_eth *eth, bool reset)
continue;
mtk_w32(eth, MAC_MCR_FORCE_LINK_DOWN, MTK_MAC_MCR(i));
- mtk_set_mcr_max_rx(netdev_priv(dev),
- dev->mtu + MTK_RX_ETH_HLEN);
+ mtk_set_mcr_max_rx(netdev_priv(dev), eth->rx_buf_len);
}
/* Indicates CDM to parse the MTK special tag from CPU
@@ -4357,14 +4427,33 @@ static int mtk_change_mtu(struct net_device *dev, int new_mtu)
int length = new_mtu + MTK_RX_ETH_HLEN;
struct mtk_mac *mac = netdev_priv(dev);
struct mtk_eth *eth = mac->hw;
+ bool running;
+ u32 rx_buf_len;
int max_mtu, i;
+ /* The RX rings are shared by every MAC, so the buffer length follows
+ * the largest MTU of all of them, including the new one.
+ */
+ max_mtu = mtk_max_gmac_mtu(eth);
+ if (new_mtu > max_mtu)
+ max_mtu = new_mtu;
+ rx_buf_len = mtk_rx_buf_len_from_mtu(max_mtu);
+
if (rcu_access_pointer(eth->prog) &&
- length > MTK_PP_MAX_BUF_SIZE) {
+ rx_buf_len > MTK_PP_MAX_BUF_SIZE) {
netdev_err(dev, "Invalid MTU for XDP mode\n");
return -EINVAL;
}
+ /* The rings cannot be resized in place while DMA is running. A
+ * larger buffer would need the reallocation at the next open; a
+ * smaller one is fine, the buffers the rings hold are already big
+ * enough for it.
+ */
+ running = refcount_read(ð->dma_refcnt);
+ if (running && rx_buf_len > eth->rx_buf_len)
+ return -EBUSY;
+
mtk_set_mcr_max_rx(mac, length);
WRITE_ONCE(dev->mtu, new_mtu);
@@ -4372,6 +4461,11 @@ static int mtk_change_mtu(struct net_device *dev, int new_mtu)
for (i = 0; i < ARRAY_SIZE(eth->ppe); i++)
mtk_ppe_update_mtu(eth->ppe[i], max_mtu);
+ if (!running) {
+ eth->rx_buf_len = rx_buf_len;
+ mtk_pdma_set_sdl(eth);
+ }
+
return 0;
}
@@ -5017,8 +5111,17 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
eth->netdev[id]->irq = eth->irq[MTK_FE_IRQ_SHARED];
eth->netdev[id]->dev.of_node = np;
+ /* DSA sets the conduit MTU (user MTU + tag) before the netdev is
+ * opened, so max_mtu must already be final here. Deriving it only
+ * in mtk_open() leaves the conduit at its 1504 default and blocks
+ * 9K frames on the DSA user ports.
+ */
if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628))
eth->netdev[id]->max_mtu = MTK_MAX_RX_LENGTH - MTK_RX_ETH_HLEN;
+ else if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_RX_9K) &&
+ mtk_interface_mode_is_xgmii(eth, phy_mode))
+ eth->netdev[id]->max_mtu =
+ MTK_MAX_RX_LENGTH_9K - MTK_RX_ETH_HLEN;
else
eth->netdev[id]->max_mtu = MTK_MAX_RX_LENGTH_2K - MTK_RX_ETH_HLEN;
@@ -5159,6 +5262,8 @@ static int mtk_probe(struct platform_device *pdev)
spin_lock_init(ð->rx_irq_lock);
spin_lock_init(ð->dim_lock);
+ eth->rx_buf_len = MTK_MAX_RX_LENGTH_2K;
+
eth->rx_dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE;
INIT_WORK(ð->rx_dim.work, mtk_dim_rx);
INIT_DELAYED_WORK(ð->reset.monitor_work, mtk_hw_reset_monitor_work);
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
index 88a9b3b23..0cf2aaf23 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -29,6 +29,7 @@
#define MTK_QDMA_PAGE_SIZE 2048
#define MTK_MAX_RX_LENGTH 1536
#define MTK_MAX_RX_LENGTH_2K 2048
+#define MTK_MAX_RX_LENGTH_9K 9216
#define MTK_TX_DMA_BUF_LEN 0x3fff
#define MTK_TX_DMA_BUF_LEN_V2 0xffff
#define MTK_QDMA_RING_SIZE 2048
@@ -201,6 +202,7 @@
/* PDMA Global Configuration Register */
#define MTK_PDMA_LRO_SDL 0x3000
#define MTK_RX_CFG_SDL_OFFSET 16
+#define MTK_RX_CFG_SDL_MASK GENMASK(30, MTK_RX_CFG_SDL_OFFSET)
/* PDMA Reset Index Register */
#define MTK_PST_DRX_IDX0 BIT(16)
@@ -530,6 +532,8 @@
/* XFI Mac control registers */
#define MTK_XMAC_BASE(x) (0x12000 + (((x) - 1) * 0x1000))
#define MTK_XMAC_MCR(x) (MTK_XMAC_BASE(x))
+#define MTK_XMAC_RX_CFG2(x) (MTK_XMAC_MCR(x) + 0xd0)
+#define MTK_XMAC_MAX_RX_MASK GENMASK(13, 0)
#define XMAC_MCR_TRX_DISABLE 0xf
#define XMAC_MCR_FORCE_TX_FC BIT(5)
#define XMAC_MCR_FORCE_RX_FC BIT(4)
@@ -985,6 +989,7 @@ enum mkt_eth_capabilities {
MTK_INFRA_BIT,
MTK_SHARED_SGMII_BIT,
MTK_HWLRO_BIT,
+ MTK_NETSYS_RX_9K_BIT,
MTK_SHARED_INT_BIT,
MTK_TRGMII_MT7621_CLK_BIT,
MTK_QDMA_BIT,
@@ -1026,6 +1031,7 @@ enum mkt_eth_capabilities {
#define MTK_INFRA BIT_ULL(MTK_INFRA_BIT)
#define MTK_SHARED_SGMII BIT_ULL(MTK_SHARED_SGMII_BIT)
#define MTK_HWLRO BIT_ULL(MTK_HWLRO_BIT)
+#define MTK_NETSYS_RX_9K BIT_ULL(MTK_NETSYS_RX_9K_BIT)
#define MTK_SHARED_INT BIT_ULL(MTK_SHARED_INT_BIT)
#define MTK_TRGMII_MT7621_CLK BIT_ULL(MTK_TRGMII_MT7621_CLK_BIT)
#define MTK_QDMA BIT_ULL(MTK_QDMA_BIT)
@@ -1127,7 +1133,7 @@ enum mkt_eth_capabilities {
#define MT7988_CAPS (MTK_36BIT_DMA | MTK_GDM1_ESW | MTK_GMAC2_2P5GPHY | \
MTK_MUX_GMAC2_TO_2P5GPHY | MTK_QDMA | MTK_RSTCTRL_PPE1 | \
- MTK_RSTCTRL_PPE2 | MTK_SRAM)
+ MTK_RSTCTRL_PPE2 | MTK_SRAM | MTK_NETSYS_RX_9K)
struct mtk_tx_dma_desc_info {
dma_addr_t addr;
@@ -1151,6 +1157,7 @@ struct mtk_reg_map {
u32 glo_cfg; /* global configuration */
u32 rst_idx; /* reset index */
u32 delay_irq; /* delay interrupt */
+ u32 rx_cfg; /* rx dma configuration */
u32 irq_status; /* interrupt status */
u32 irq_mask; /* interrupt mask */
u32 adma_rx_dbg0;
@@ -1314,6 +1321,7 @@ struct mtk_eth {
struct mtk_tx_ring tx_ring;
struct mtk_rx_ring rx_ring[MTK_MAX_RX_RING_NUM];
struct mtk_rx_ring rx_ring_qdma;
+ u32 rx_buf_len;
struct napi_struct tx_napi;
struct napi_struct rx_napi;
void *scratch_ring;
--
2.53.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH net-next v3 0/2] net: ethernet: mtk_eth_soc: 9K jumbo frame support
@ 2026-11-08 20:38 Sandeep Haemoon
2026-11-08 20:37 ` [PATCH net-next v3 1/2] net: ethernet: mtk_eth_soc: skip page_pool Sandeep Haemoon
2026-11-08 20:38 ` [PATCH net-next v3 2/2] net: ethernet: mtk_eth_soc: add 9K jumbo Sandeep Haemoon
0 siblings, 2 replies; 5+ messages in thread
From: Sandeep Haemoon @ 2026-11-08 20:38 UTC (permalink / raw)
To: netdev
Cc: Felix Fietkau, Lorenzo Bianconi, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Matthias Brugger,
AngeloGioacchino Del Regno, Russell King, Mason Chang,
Frank Wunderlich, linux-mediatek, linux-arm-kernel, linux-kernel
This series makes the MediaTek SoC ethernet driver support 9K jumbo
frames on the 10G XGMII MACs (MT7988). The driver advertises a 2030-byte
max_mtu and programs the MAC max-rx field for at most 2048 bytes, so
MTU > 2K is rejected and 9K frames are not usable.
Patch 1 gates page_pool on the descriptor length of the ring: the pool
hands out order-0 pages and maps at most MTK_PP_MAX_BUF_SIZE, so a ring
whose descriptor length is larger has to use the
netdev_alloc_frag()/mtk_max_lro_buf_alloc() path. It is pure
preparation, every ring fits today.
Patch 2 adds the MTK_NETSYS_RX_9K capability, advertises 9K max_mtu on
the XGMII MACs (from the DT phy_mode at probe, so DSA can raise the
conduit MTU before the netdev is opened), programs XMAC_RX_CFG2 and the
PDMA RX SDL, derives the shared RX buffer length from the largest MTU,
sizes the RX rings from it and keeps the MAC max-rx limits in sync with
it. A runtime MTU change that needs larger buffers is rejected with
-EBUSY while DMA is running; shrinking is allowed.
This work is a forward-port of the MediaTek downstream jumbo work: Mason
Chang's "change default rx buffer length" and "add 9k jumbo frame
support" (MediaTek OpenWrt feed 999-eth-30/31, carried in
frank-w/BPI-Router-Linux), plus Frank Wunderlich's "try to fix crash the
right way". The runtime reallocation of Mason's "dynamic rx buffer
adjustment" (feed 999-eth-33) is not included and will be sent
separately.
The equivalent change was tested on a BPI-R4 Pro 8X (MT7988, 10G lan6 /
MxL86252) on the 6.18.60 backport: with both ends at MTU 9000, 8K ICMP
passes and iperf3 with MSS 8900 reaches ~9.87 Gbit/s (vs ~2.57 Gbit/s at
MSS 1460); the DSA conduit is raised to 9004 at boot. This v7.3 series
is compile-tested with CONFIG_NET_MEDIATEK_SOC=y and COMPILE_TEST=y; no
hardware run of this exact tree yet.
Changes in v3:
- gate page_pool on ring->buf_size <= MTK_PP_MAX_BUF_SIZE instead of
ring->frag_size <= PAGE_SIZE: frag_size uses the netdev_alloc_frag()
layout, so a ring could pass the old test while its descriptor length
was already larger than the pool can map;
- keep 2K as the minimum RX buffer length, so MTUs up to 2K keep working
at runtime on every SoC, DSA user ports included;
- restore MT7628's 1518 max_mtu, which v2 dropped by accident;
- in mtk_change_mtu() reject only a change that needs larger buffers, so
a lowered MTU no longer locks the port out of further changes;
- program XMAC_RX_CFG2 with read-modify-write, keeping the bits outside
the length field;
- replace the PDMA RX SDL field instead of OR-ing into it, which grew on
every open with a different MTU;
- rewrite the MAC max-rx limits on every mac_config(), so a reopen with a
smaller MTU narrows MACs that still carry the old limit;
- derive the XDP admission checks from the shared RX buffer length, so a
program cannot attach and then never run;
- correct the commit messages: no runtime reallocation is included, and
mtk_hw_init() now uses the shared length too;
- keep Simon Horman's Reviewed-by on 1/2 only, as 2/2 changed materially.
The order-2 allocation cost at 9K (one buffer per slot, high-order
GFP_ATOMIC refill) is unchanged from the downstream design and is called
out in the commit message.
v2: https://lore.kernel.org/all/patch-2793186c653124c4c0b3d463bde2ea3a@loljews.com/
v1: https://lore.kernel.org/all/patch-4f863a941889f0bceca1c082f2d79624@loljews.com/
Sandeep Haemoon (2):
net: ethernet: mtk_eth_soc: skip page_pool for RX rings it cannot back
net: ethernet: mtk_eth_soc: add 9K jumbo frame support
drivers/net/ethernet/mediatek/mtk_eth_soc.c | 215 +++++++++++++++-----
drivers/net/ethernet/mediatek/mtk_eth_soc.h | 10 +-
2 files changed, 173 insertions(+), 52 deletions(-)
base-commit: c03c1a14507dfaf8b856c2608f9ac216021fc222
--
2.53.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH net-next v3 1/2] net: ethernet: mtk_eth_soc: skip page_pool
2026-11-08 20:37 ` [PATCH net-next v3 1/2] net: ethernet: mtk_eth_soc: skip page_pool Sandeep Haemoon
@ 2026-11-09 22:19 ` netdev-bot+sashiko
0 siblings, 0 replies; 5+ messages in thread
From: netdev-bot+sashiko @ 2026-11-09 22:19 UTC (permalink / raw)
To: sandeeph
Cc: netdev, nbd, lorenzo, andrew+netdev, davem, edumazet, kuba,
pabeni, matthias.bgg, angelogioacchino.delregno, linux,
mason-cw.chang, frank-w, linux-mediatek, linux-arm-kernel,
linux-kernel
Thank you for your contribution! Sashiko AI review found 3 potential
issue(s) to consider.
Critical: 0 (1) · High: 0 · Medium: 1 · Low: 2
If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).
- [Medium] The new gate in mtk_rx_alloc() (`mtk_page_pool_enabled(eth) &&
ring->buf_size <= MTK_PP_MAX_BUF_SIZE`) means a netsys v2+ ring can…
- [Low] The commit message does not match the code.
- [Low] The new comment in mtk_rx_alloc() describes changelog history
rather than the code.
Pre-existing issues:
- [Critical] This problem wasn't introduced by this patch, but in
mtk_poll_rx() the release_desc path rebuilds the descriptor's high DMA…
--
net: ethernet: mtk_eth_soc: skip page_pool
This commit adds a ring->buf_size <= MTK_PP_MAX_BUF_SIZE condition to the
page_pool creation in mtk_rx_alloc(). Rings whose descriptor length is
larger than the pool can back fall back to netdev_alloc_frag() or
mtk_max_lro_buf_alloc(). It is described as preparation for 9K jumbo
support in the next patch, with no functional change at this commit.
Link: https://github.com/mediatek/mtk-openwrt-feeds
> net: ethernet: mtk_eth_soc: skip page_pool
[Severity: Low]
This isn't a bug, but the subject line looks cut off. It doesn't say when
the page_pool is skipped.
> Gate the pool on ring->buf_size instead of ring->frag_size: frag_size is
> computed with the netdev_alloc_frag() layout, whose headroom and
> skb_shared_info footprint differ from the pool's, so a ring can pass a
> frag_size <= PAGE_SIZE test while its descriptor length is already larger
> than the pool can map.
Is "instead of ring->frag_size" accurate? Before this patch, the only
gate on the pool was:
if (mtk_page_pool_enabled(eth)) {
The ring->frag_size <= PAGE_SIZE test exists only in the non-page_pool
branch. There it chooses between netdev_alloc_frag() and
mtk_max_lro_buf_alloc().
So this patch adds a new size condition rather than replacing a frag_size
one. The frag_size vs. buf_size reasoning and the no-functional-change
claim both look correct. Could the wording avoid implying that a
frag_size gate existed before?
> diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
> index ad0fdc4133fb6..f2b2fb1130ff9 100644
> --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
> +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
> @@ -2819,7 +2819,15 @@ static int mtk_rx_alloc(struct mtk_eth *eth, int ring_no, int rx_flag)
> if (!ring->data)
> return -ENOMEM;
>
> - if (mtk_page_pool_enabled(eth)) {
> + /* page_pool hands out order-0 pages and caps the DMA mapping at
> + * MTK_PP_MAX_BUF_SIZE, so it can only back a ring whose descriptor
> + * length fits that. Today every ring does; this is the preparation
> + * for the 9K support in the next patch, where the normal ring grows
> + * past the pool's limit and takes the netdev_alloc_frag()/
> + * mtk_max_lro_buf_alloc() path below instead.
> + */
[Severity: Low]
This isn't a bug, but this comment describes the series history rather
than the code. The follow-up "net: ethernet: mtk_eth_soc: add 9K jumbo"
leaves it unchanged. After that patch:
- "Today every ring does" is no longer true. A 9K ring has buf_size
9216 and no pool.
- "the next patch" no longer refers to anything.
- mtk_max_lro_buf_alloc() has been renamed to mtk_max_buf_alloc().
Also, does page_pool really cap the DMA mapping at MTK_PP_MAX_BUF_SIZE?
mtk_create_page_pool() uses:
.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV,
...
.offset = MTK_PP_HEADROOM,
.max_len = MTK_PP_MAX_BUF_SIZE,
PP_FLAG_DMA_MAP maps the whole order-0 page. max_len only limits the
sync-for-device length. The real constraint seems to be that
RX_DMA_PREP_PLEN0(ring->buf_size) must not exceed offset + max_len.
> + if (mtk_page_pool_enabled(eth) &&
> + ring->buf_size <= MTK_PP_MAX_BUF_SIZE) {
[Severity: Medium]
With this condition, a netsys v2+ ring can now have
ring->page_pool == NULL. In mtk_poll_rx(), XDP only runs in the
page_pool branch:
if (ring->page_pool) {
...
ret = mtk_xdp_run(eth, ring, &xdp, netdev);
...
} else {
if (ring->frag_size <= PAGE_SIZE)
new_data = napi_alloc_frag(ring->frag_size);
If an XDP program is accepted while the ring has no pool, the program
silently never runs.
This can't happen at this commit, because buf_size is always 2048. With
the follow-up "net: ethernet: mtk_eth_soc: add 9K jumbo" applied,
mtk_xdp_setup() checks the rx_buf_len recomputed from the current MTUs:
if (dev->mtu > MTK_PP_MAX_BUF_SIZE ||
mtk_rx_buf_len(eth) > MTK_PP_MAX_BUF_SIZE) {
It does not check eth->rx_buf_len, which the live rings were allocated
with, or ring->page_pool itself.
Can this sequence on MT7988 with two GMACs attach an XDP program that
never runs?
1. With both interfaces down, set eth1 MTU to 9000. mtk_change_mtu()
takes the !running branch and sets eth->rx_buf_len = 9216.
2. Bring eth0 and eth1 up. buf_size is 9216, so no page_pool is created.
3. While running, set eth1 MTU back to 1500. mtk_change_mtu() allows it:
running = refcount_read(ð->dma_refcnt);
if (running && rx_buf_len > eth->rx_buf_len)
return -EBUSY;
eth->rx_buf_len stays at 9216.
4. Attach XDP to eth0. mtk_xdp_setup() sees dev->mtu 1500 and
mtk_rx_buf_len(eth) 2048, and accepts the program. Its mtk_stop() and
mtk_open() calls only move dma_refcnt 2->1->2, because eth1 is still
up:
if (!refcount_dec_and_test(ð->dma_refcnt))
return 0;
So the rings are not reallocated.
The attach returns success, but all RX traffic bypasses the program.
xdp_features is also still advertised based on mtk_page_pool_enabled()
alone.
Could mtk_xdp_setup() check eth->rx_buf_len, or whether the rings have a
page_pool, instead?
> struct page_pool *pp;
>
> pp = mtk_create_page_pool(eth, &ring->xdp_q, ring_no,
[Severity: Critical]
This is a pre-existing issue and wasn't introduced by this patch. While
looking at the fallback path that this series moves MT7988 jumbo rings
onto, it looks like mtk_poll_rx() can hand a descriptor back to the
hardware with a mixed DMA address.
dma_addr is set once, outside the loop:
dma_addr_t dma_addr = DMA_MAPPING_ERROR;
The release_desc path rebuilds the high address bits from it:
skip_rx:
ring->data[idx] = new_data;
rxd->rxd1 = (unsigned int)dma_addr;
release_desc:
if (MTK_HAS_CAPS(eth->soc->caps, MTK_36BIT_DMA)) {
if (unlikely(dma_addr == DMA_MAPPING_ERROR))
addr64 = FIELD_GET(RX_DMA_ADDR64_MASK,
rxd->rxd2);
else
addr64 = RX_DMA_PREP_ADDR64(dma_addr);
}
Suppose iteration N refills its slot with buffer X, which leaves
dma_addr set to X. Iteration N+1 can then jump straight to release_desc
in three cases:
- there is no netdev for the source port
- MTK_RESETTING is set
- a GFP_ATOMIC allocation fails in mtk_page_pool_get_buff() or
napi_alloc_frag()
mtk_page_pool_get_buff() only writes *dma_addr on success. In that case
rxd1 still holds lo(Y) for the buffer Y the slot owns, but rxd2 gets
hi(X).
On MTK_36BIT_DMA SoCs with DRAM above 4 GiB, can the NIC then DMA the
next frame to hi(X)|lo(Y), which is neither buffer? The driver would
later call dma_unmap_single() on that address and pass the stale
contents of Y up the stack.
Should dma_addr be reset to DMA_MAPPING_ERROR at the start of each loop
iteration?
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/patch-aad4cfa8d9522d1f7dbc9893ea12e813%40loljews.com
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH net-next v3 2/2] net: ethernet: mtk_eth_soc: add 9K jumbo
2026-11-08 20:38 ` [PATCH net-next v3 2/2] net: ethernet: mtk_eth_soc: add 9K jumbo Sandeep Haemoon
@ 2026-11-09 22:19 ` netdev-bot+sashiko
0 siblings, 0 replies; 5+ messages in thread
From: netdev-bot+sashiko @ 2026-11-09 22:19 UTC (permalink / raw)
To: sandeeph
Cc: netdev, nbd, lorenzo, andrew+netdev, davem, edumazet, kuba,
pabeni, matthias.bgg, angelogioacchino.delregno, linux,
mason-cw.chang, frank-w, linux-mediatek, linux-arm-kernel,
linux-kernel
Thank you for your contribution! Sashiko AI review found 11 potential
issue(s) to consider.
Critical: 0 · High: 1 · Medium: 5 · Low: 5
If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).
- [High] mtk_xdp_setup() can accept an XDP program that then never runs.
- [Medium] The patch changes the GMAC MCR max-RX limit on every non-
MT7628 SoC, not just MT7988.
- [Medium] mtk_pdma_set_sdl() writes `FIELD_PREP(GENMASK(30,16),
MTK_PDMA_LRO_SDL + eth->rx_buf_len)` into PDMA RX_CFG (0x6a10) on…
- [Medium] Normal and QDMA RX rings now use high-order compound pages
whenever frag_size > PAGE_SIZE: `mtk_max_buf_alloc(ring->frag_size,…
- [Medium] On MT7988, GMAC1 (built-in switch, DT phy-mode 'internal')
counts as XGMII in mtk_interface_mode_is_xgmii(), so mtk_add_mac()…
- [Medium] In the XGMII branch, mtk_set_mcr_max_rx() writes the exact
byte value it receives into MTK_XMAC_RX_CFG2[13:0] (`min_t(u32, val,…
- [Low] The commit message says the patch programs the PDMA RX SDL
'replacing the field instead of OR-ing into the value already there',…
- [Low] The stated motivation is to let DSA raise the conduit MTU for 9K,
but the -EBUSY check in mtk_change_mtu() (lines 4453-4455) tests the…
- [Low] The commit message says the patch is a forward-port of Mason
Chang's 'change default rx buffer length' and 'add 9k jumbo frame…
- [Low] The comment above the page_pool gate in mtk_rx_alloc() (lines
2865-2871) is now wrong in two ways.
- [Low] The comment in mtk_dma_init() (lines 3360-3364) says
eth->rx_buf_len is 'Derived here, at ring allocation, so…
--
net: ethernet: mtk_eth_soc: add 9K jumbo
This adds a MTK_NETSYS_RX_9K capability for MT7988 and advertises a 9K
max_mtu on the XGMII MACs. The shared RX rings are sized from the largest
MTU of any MAC, and the GMAC/XMAC max-rx limits and the PDMA RX SDL are
programmed from that shared length. While DMA is running, MTU increases
that need larger buffers fail with -EBUSY, and XDP is rejected when the
buffer length is larger than MTK_PP_MAX_BUF_SIZE.
Link: https://github.com/frank-w/BPI-Router-Linux/commit/b4c6cb1d5532
Link: https://github.com/mediatek/mtk-openwrt-feeds
> - program the PDMA RX SDL max frame from the shared buffer length,
> replacing the field instead of OR-ing into the value already there;
[Severity: Low]
Is this description accurate? In the baseline, MTK_PDMA_LRO_SDL and
MTK_RX_CFG_SDL_OFFSET are only defined in mtk_eth_soc.h. Nothing in
mtk_eth_soc.c uses them, and this patch adds the .rx_cfg reg map entry.
So there was no OR-ing to replace. The patch adds a new write to PDMA
RX_CFG through mtk_pdma_set_sdl(). It runs on every MT7988 DMA init and
on MTU changes while DMA is down, including at the default 1500 MTU.
Could the commit message say this is a new register write and not a fix
to existing behaviour?
> This is a forward-port of the MediaTek downstream jumbo work: Mason
> Chang's "change default rx buffer length" and "add 9k jumbo frame
> support" (MediaTek OpenWrt feed 999-eth-30/31, carried in
> frank-w/BPI-Router-Linux), plus Frank Wunderlich's "try to fix crash the
> right way".
[ ... ]
> Signed-off-by: Sandeep Haemoon <sandeeph@loljews.com>
[Severity: Low]
Should this carry credit tags for the original authors? The only trailer
is the submitter's Signed-off-by. There is no Co-developed-by: and
Signed-off-by: pair for Mason Chang or Frank Wunderlich, and no original
From:. Documentation/process/submitting-patches.rst asks for these when
porting someone else's work.
> diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
> index f2b2fb1130ff9..c7bc8eaea7ab0 100644
> --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
> +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
[ ... ]
> @@ -554,6 +555,41 @@ static int mtk_mac_prepare(struct phylink_config *config, unsigned int mode,
> return 0;
> }
>
> +static void mtk_set_mcr_max_rx(struct mtk_mac *mac, u32 val)
> +{
> + struct mtk_eth *eth = mac->hw;
> + u32 mcr_cur, mcr_new;
> +
> + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628))
> + return;
> +
> + if (!mtk_interface_mode_is_xgmii(eth, mac->interface)) {
> + mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
> + mcr_new = mcr_cur & ~MAC_MCR_MAX_RX_MASK;
[ ... ]
> + } else if (mtk_is_netsys_v3_or_greater(mac->hw) &&
> + mac->id != MTK_GMAC1_ID) {
[Severity: Medium]
After this patch, does anything program an RX length limit for GMAC1 on
MT7988? GMAC1 is the built-in switch port with phy-mode internal, and
mtk_interface_mode_is_xgmii() treats PHY_INTERFACE_MODE_INTERNAL as
XGMII.
Once mtk_mac_config() sets mac->interface to INTERNAL, the MCR branch
above is skipped because the interface is XGMII. This branch is then
skipped by the mac->id != MTK_GMAC1_ID check.
Next, mtk_mac_config() does:
if (mtk_interface_mode_is_xgmii(eth, state->interface)) {
mtk_w32(mac->hw, MTK_GDMA_XGDM_SEL, MTK_GDMA_EG_CTRL(mac->id));
mtk_w32(mac->hw, MAC_MCR_FORCE_LINK_DOWN, MTK_MAC_MCR(mac->id));
That leaves MAX_RX in MTK_MAC_MCR(0) at the 1518 encoding.
mtk_xgdm_mac_link_up() also returns early for GMAC1. In the baseline,
mtk_change_mtu() wrote MAX_RX into MTK_MAC_MCR(0).
At the same time, mtk_add_mac() advertises a 9K max_mtu for GMAC1,
because INTERNAL counts as XGMII.
Depending on the hardware, one of two things seems to follow. Either 9K
never works on the DSA conduit, or GMAC1 has no programmed limit at all.
In the second case, does the commit message's "no MAC accepts a frame
the rings cannot hold" hold for this MAC?
> + mcr_cur = mtk_r32(mac->hw, MTK_XMAC_RX_CFG2(mac->id));
> + mcr_new = mcr_cur & ~MTK_XMAC_MAX_RX_MASK;
> + mcr_new |= FIELD_PREP(MTK_XMAC_MAX_RX_MASK,
> + min_t(u32, val, MTK_MAX_RX_LENGTH_9K));
[Severity: Medium]
Unlike the coarse buckets used for the GMAC MCR, this writes the exact
byte value into XMAC_RX_CFG2. mtk_change_mtu() passes:
int length = new_mtu + MTK_RX_ETH_HLEN;
and MTK_RX_ETH_HLEN is ETH_HLEN + ETH_FCS_LEN, with no VLAN_HLEN.
Shrinking the MTU is allowed while running. After a runtime shrink to
MTU 1500 on MT7988 GMAC2, the XMAC limit becomes 1518. Would full-size
802.1Q tagged frames (1522 bytes) then be dropped by the MAC, even
though the rings hold at least 2048 bytes?
Likewise, at max_mtu the limit is capped at 9216, so tagged frames at
max_mtu could not be received either.
mtk_mac_config() also writes eth->rx_buf_len (at least 2048) into this
register on every open. The effective limit then depends on whether the
last operation was an MTU change or a reopen.
[ ... ]
> @@ -649,6 +685,13 @@ static void mtk_mac_config(struct phylink_config *config, unsigned int mode,
> mac->interface = state->interface;
> }
>
> + /* What a MAC may accept is a property of the shared RX rings, so
> + * rewrite the limit on every mac_config and not only when the
> + * interface mode changes: a reopen after an MTU change must narrow
> + * the MACs that still carry the previous, larger limit.
> + */
> + mtk_set_mcr_max_rx(mac, eth->rx_buf_len);
[Severity: Medium]
Is it intended that this affects every non-MT7628 SoC, and not only
MT7988?
mtk_rx_buf_len_from_mtu() never returns less than MTK_MAX_RX_LENGTH_2K.
Every open goes through
phylink_start()->phylink_mac_initial_config()->mtk_mac_config(). So on
MT7621/7622/7623/7629/7981/7986 the GMAC MCR now gets
MAC_MCR_MAX_RX_2048 at any MTU.
In the baseline, mtk_mac_config() did not touch this field, and
mtk_hw_init() used dev->mtu + MTK_RX_ETH_HLEN. A 1500 MTU interface got
the 1518 bucket and a 1504 DSA conduit got 1536. mtk_hw_init() now uses
eth->rx_buf_len as well.
So on all of these SoCs, frames up to 2048 bytes are accepted at a 1500
MTU, where before they were dropped and counted as rx_long_errors. The
commit message doesn't seem to mention this change.
mtk_change_mtu() also still calls:
mtk_set_mcr_max_rx(mac, length);
with the per-device new_mtu + MTK_RX_ETH_HLEN. Doesn't that make the
same MTU give a different hardware limit, depending on whether an MTU
change or a reopen happened last?
[ ... ]
> @@ -2310,7 +2352,8 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget,
> if (ring->frag_size <= PAGE_SIZE)
> new_data = napi_alloc_frag(ring->frag_size);
> else
> - new_data = mtk_max_lro_buf_alloc(GFP_ATOMIC);
> + new_data = mtk_max_buf_alloc(ring->frag_size,
> + GFP_ATOMIC);
[Severity: Medium]
With a 9K MTU, the normal and QDMA rings now take this path.
mtk_max_buf_alloc() allocates whole pages:
data = __get_free_pages(gfp_mask | __GFP_COMP | __GFP_NOWARN,
get_order(size));
For a frag_size of about 9.6K that is 16 KiB. Later in mtk_poll_rx(),
the skb is still built with:
skb = build_skb(data, ring->frag_size);
So skb->truesize comes from frag_size, not from the 16 KiB actually
allocated. Does that under-report the memory pinned by each skb by
about 40%? Remote traffic could then pin about 1.7x what the socket
rcvbuf and tcp_mem limits allow.
Before this patch only the HWLRO rings had this mismatch. Now it covers
every RX buffer once any MAC's MTU is above about 3.5K.
[ ... ]
> @@ -2812,8 +2855,8 @@ static int mtk_rx_alloc(struct mtk_eth *eth, int ring_no, int rx_flag)
> rx_dma_size = soc->rx.dma_size;
> }
>
> - ring->frag_size = mtk_max_frag_size(rx_data_len);
> - ring->buf_size = mtk_max_buf_size(ring->frag_size);
> + ring->frag_size = mtk_max_frag_size(eth, rx_data_len);
> + ring->buf_size = mtk_max_buf_size(eth, ring->frag_size);
[Severity: Low]
This isn't a bug, but the comment a few lines below, above the page_pool
check in mtk_rx_alloc(), is now stale:
* length fits that. Today every ring does; this is the preparation
* for the 9K support in the next patch, where the normal ring grows
* past the pool's limit and takes the netdev_alloc_frag()/
* mtk_max_lro_buf_alloc() path below instead.
This patch renames mtk_max_lro_buf_alloc() to mtk_max_buf_alloc(). This
patch is also the 9K support. With eth->rx_buf_len up to 9216, not every
ring fits MTK_PP_MAX_BUF_SIZE any more.
[ ... ]
> @@ -3256,6 +3300,55 @@ static int mtk_dma_busy_wait(struct mtk_eth *eth)
[ ... ]
> +static void mtk_pdma_set_sdl(struct mtk_eth *eth)
> +{
> + const struct mtk_reg_map *reg_map = eth->soc->reg_map;
> + u32 val;
> +
> + if (!mtk_is_netsys_v3_or_greater(eth))
> + return;
> +
> + val = mtk_r32(eth, reg_map->pdma.rx_cfg);
> + val &= ~MTK_RX_CFG_SDL_MASK;
> + val |= FIELD_PREP(MTK_RX_CFG_SDL_MASK,
> + MTK_PDMA_LRO_SDL + eth->rx_buf_len);
> + mtk_w32(eth, val, reg_map->pdma.rx_cfg);
[Severity: Medium]
Is MTK_PDMA_LRO_SDL + eth->rx_buf_len the intended value? The comment in
mtk_dma_init() says the SDL is programmed "from rx_buf_len so jumbo
frames up to it are accepted by the DMA engine".
The code writes 0x3800 (14336) at 2K and 0x5400 (21504) at 9K. That is
about 12 KiB more than both rx_buf_len and the per-descriptor
ring->buf_size.
MTK_PDMA_LRO_SDL is 0x3000, the same as MTK_MAX_LRO_RX_LENGTH. That is
an HW LRO constant, and MT7988 does not use HW LRO here.
The new MTK_RX_CFG_SDL_MASK, GENMASK(30, 16), sits under the "PDMA Global
Configuration Register" comment in mtk_eth_soc.h but is applied to
rx_cfg. At 9K, + and | give different values (0x5400 vs 0x3400), and the
write sets bit 30. If the real field is narrower, could this overwrite a
neighbouring RX_CFG control bit?
This register was never written before, so every MT7988 board now loses
its reset default, even at the default 1500 MTU. Is the field layout
documented somewhere?
[ ... ]
> @@ -3784,7 +3877,8 @@ static int mtk_xdp_setup(struct net_device *dev, struct bpf_prog *prog,
> return -EOPNOTSUPP;
> }
>
> - if (dev->mtu > MTK_PP_MAX_BUF_SIZE) {
> + if (dev->mtu > MTK_PP_MAX_BUF_SIZE ||
> + mtk_rx_buf_len(eth) > MTK_PP_MAX_BUF_SIZE) {
[Severity: High]
Can this accept an XDP program that never runs?
mtk_rx_buf_len() recomputes the length from the current MTUs, but the
live rings were built from eth->rx_buf_len. mtk_change_mtu() allows a
shrink while DMA is running and leaves eth->rx_buf_len unchanged, so the
two values can differ.
On MT7988:
1. Set GMAC2 to MTU 9000 while it is down, then bring up GMAC2 and
GMAC1. mtk_dma_init() sets eth->rx_buf_len to 9216, so
ring->buf_size is larger than MTK_PP_MAX_BUF_SIZE and
mtk_rx_alloc() creates no page_pool.
2. Run "ip link set <gmac2> mtu 1500" while it is running. The old 9000
MTU still gives 9216, which is not larger than eth->rx_buf_len, so
there is no -EBUSY and the rings stay at 9K.
3. Attach XDP to either netdev. dev->mtu is 1500 and mtk_rx_buf_len(eth)
is 2048, so this check passes.
If the target is running, mtk_stop() returns early here, because the
other MAC still holds a reference:
if (!refcount_dec_and_test(ð->dma_refcnt))
return 0;
mtk_open() then only does refcount_inc(). If the target is down,
nothing is reopened. Either way the rings are not rebuilt.
mtk_poll_rx() only calls mtk_xdp_run() inside if (ring->page_pool). So
every packet goes through build_skb() and up the stack while the program
shows as attached.
The commit message says this case is rejected "instead of letting a
program attach and never run". Should this check also look at
eth->rx_buf_len, or at whether the live rings have a page_pool?
[ ... ]
> @@ -4357,14 +4427,33 @@ static int mtk_change_mtu(struct net_device *dev, int new_mtu)
> int length = new_mtu + MTK_RX_ETH_HLEN;
> struct mtk_mac *mac = netdev_priv(dev);
> struct mtk_eth *eth = mac->hw;
> + bool running;
> + u32 rx_buf_len;
> int max_mtu, i;
>
> + /* The RX rings are shared by every MAC, so the buffer length follows
> + * the largest MTU of all of them, including the new one.
> + */
> + max_mtu = mtk_max_gmac_mtu(eth);
> + if (new_mtu > max_mtu)
> + max_mtu = new_mtu;
> + rx_buf_len = mtk_rx_buf_len_from_mtu(max_mtu);
[ ... ]
> + running = refcount_read(ð->dma_refcnt);
> + if (running && rx_buf_len > eth->rx_buf_len)
> + return -EBUSY;
[Severity: Low]
running comes from the DMA refcount that all MACs share, not from
whether this netdev is up.
On MT7988 boards where any other GMAC is up (for example a WAN port on
GMAC2 or GMAC3), raising the DSA conduit to a jumbo MTU returns -EBUSY,
even if the conduit itself is down. dsa_user_change_mtu() passes that
error back to the user port.
DSA brings the conduit up whenever a user port is up. Wouldn't raising a
user port MTU on a live system then always fail, so that 9K DSA only
works once every GMAC is down?
The commit message describes the -EBUSY rule, but not that it limits the
DSA flow it is meant to enable.
> +
> mtk_set_mcr_max_rx(mac, length);
> WRITE_ONCE(dev->mtu, new_mtu);
>
> @@ -4372,6 +4461,11 @@ static int mtk_change_mtu(struct net_device *dev, int new_mtu)
> for (i = 0; i < ARRAY_SIZE(eth->ppe); i++)
> mtk_ppe_update_mtu(eth->ppe[i], max_mtu);
>
> + if (!running) {
> + eth->rx_buf_len = rx_buf_len;
> + mtk_pdma_set_sdl(eth);
> + }
[Severity: Low]
The comment in mtk_dma_init() says eth->rx_buf_len is "Derived here, at
ring allocation, so eth->rx_buf_len always describes the buffers the
rings actually hold". Here it is assigned while no rings exist.
rx_buf_len is also computed from mtk_max_gmac_mtu() before
WRITE_ONCE(dev->mtu, new_mtu), so this device's old MTU still counts.
For example, shrinking the only 9000 MTU device to 1500 while it is down
leaves eth->rx_buf_len at 9216 and the SDL programmed for 9216.
mtk_xdp_setup() computes 2048 from the new MTUs, so the two disagree
until the next mtk_dma_init() recomputes the value.
Should this use the new MTUs, or leave eth->rx_buf_len to mtk_dma_init()?
> return 0;
> }
[ ... ]
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/patch-aad4cfa8d9522d1f7dbc9893ea12e813%40loljews.com
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-11-09 22:19 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-08 20:38 [PATCH net-next v3 0/2] net: ethernet: mtk_eth_soc: 9K jumbo frame support Sandeep Haemoon
2026-11-08 20:37 ` [PATCH net-next v3 1/2] net: ethernet: mtk_eth_soc: skip page_pool Sandeep Haemoon
2026-11-09 22:19 ` netdev-bot+sashiko
2026-11-08 20:38 ` [PATCH net-next v3 2/2] net: ethernet: mtk_eth_soc: add 9K jumbo Sandeep Haemoon
2026-11-09 22:19 ` netdev-bot+sashiko
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®