[PATCH net-next 9/9] net: sparx5/lan969x: add support for changing the MTU
From: Daniel Machon
Date: Thu Nov 05 2026 - 09:04:26 EST
The MTU of a port is currently fixed to 1500, as the MAC maximum frame
length, the FDMA buffers, the queue watermarks and the TAS guard band
are all sized for a 1518 byte frame. The previous patches make the
buffers, watermarks and guard band follow the MTU, so changing the MTU
is a matter of programming the MAC and updating these.
Add an ndo_change_mtu callback that sets the MAC maximum frame length of
the port and resizes the FDMA buffers. The MAC must never accept frames
that are larger than the RX buffers, so when decreasing the MTU, the
maximum frame length is lowered before the buffers are shrunk, and when
increasing it, it is only raised once the buffers have been grown. The
new MTU is only published once the buffers fit it. If the buffers cannot
be grown, the MTU is not changed. If they cannot be shrunk, the current
buffers still fit all frames, so the new MTU is kept. Once the new MTU
is published, update the queue watermarks of the port and the TAS guard
band.
As the FDMA is shared by all ports, resizing its buffers briefly stops
CPU traffic on all ports. This only happens when the change affects the
buffer size needed for the largest MTU of the ports.
Register based extraction, which is used when the FDMA is not, sizes
its skbs from the MTU at the time a frame is read, so frames queued
before an MTU decrease would not fit. Therefore, only allow the MTU to be
changed when the FDMA is used.
The MACs of both Sparx5 and lan969x support frames of up to 14000 bytes,
so set the maximum MTU accordingly when the FDMA is used.
Signed-off-by: Daniel Machon <daniel.machon@xxxxxxxxxxxxx>
---
.../net/ethernet/microchip/sparx5/sparx5_main.h | 1 +
.../net/ethernet/microchip/sparx5/sparx5_netdev.c | 48 +++++++++++++++++++++-
2 files changed, 48 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
index d150a76fc917e..181637b5f8f4a 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
@@ -110,6 +110,7 @@ enum sparx5_feature {
#define SPX5_BUFFER_MEMORY 4194280 /* 22795 words * 184 bytes */
#define SPX5_TAS_ENTRIES_PER_PORT 2 /* TAS lists per port */
#define SPX5_HW_MTU(mtu) ((mtu) + ETH_HLEN + ETH_FCS_LEN)
+#define SPX5_MAX_FRAME_LEN 14000 /* Max frame length supported by the MACs */
#define XTR_QUEUE 0
#define INJ_QUEUE 0
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
index b3725cf2dd44c..095925752f5f6 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
@@ -250,6 +250,44 @@ static int sparx5_port_hwtstamp_set(struct net_device *dev,
return sparx5_ptp_hwtstamp_set(sparx5_port, cfg, extack);
}
+static int sparx5_port_change_mtu(struct net_device *dev, int new_mtu)
+{
+ struct sparx5_port *port = netdev_priv(dev);
+ struct sparx5 *sparx5 = port->sparx5;
+ bool resize = new_mtu > dev->mtu;
+ int err;
+
+ /* Register based extraction sizes its skbs from the MTU at the time
+ * a frame is read, so it cannot handle frames queued before an MTU
+ * decrease.
+ */
+ if (sparx5->fdma_irq <= 0)
+ return -EOPNOTSUPP;
+
+ /* Never let the MAC accept frames larger than the RX buffers */
+ if (!resize)
+ sparx5_port_max_len_set(sparx5, port, SPX5_HW_MTU(new_mtu));
+
+ err = sparx5->data->ops->fdma_resize(sparx5, port, new_mtu);
+ if (err) {
+ if (resize)
+ return err;
+
+ /* The current, larger, buffers still fit all frames */
+ netdev_warn(dev, "Failed to resize FDMA buffers: %d\n", err);
+ }
+
+ WRITE_ONCE(dev->mtu, new_mtu);
+
+ sparx5_port_wm_update(sparx5, port);
+ sparx5_tas_gb_update(sparx5);
+
+ if (resize)
+ sparx5_port_max_len_set(sparx5, port, SPX5_HW_MTU(new_mtu));
+
+ return 0;
+}
+
static const struct net_device_ops sparx5_port_netdev_ops = {
.ndo_open = sparx5_port_open,
.ndo_stop = sparx5_port_stop,
@@ -257,6 +295,7 @@ static const struct net_device_ops sparx5_port_netdev_ops = {
.ndo_set_rx_mode_async = sparx5_set_rx_mode,
.ndo_get_phys_port_name = sparx5_port_get_phys_port_name,
.ndo_set_mac_address = sparx5_set_mac_address,
+ .ndo_change_mtu = sparx5_port_change_mtu,
.ndo_validate_addr = eth_validate_addr,
.ndo_get_stats64 = sparx5_get_stats64,
.ndo_get_port_parent_id = sparx5_get_port_parent_id,
@@ -306,7 +345,14 @@ int sparx5_register_netdevs(struct sparx5 *sparx5)
for (portno = 0; portno < sparx5->data->consts->n_ports; portno++)
if (sparx5->ports[portno]) {
- err = register_netdev(sparx5->ports[portno]->ndev);
+ struct net_device *ndev = sparx5->ports[portno]->ndev;
+
+ /* The MTU can only be changed when FDMA is used */
+ if (sparx5->fdma_irq > 0)
+ ndev->max_mtu = SPX5_MAX_FRAME_LEN - ETH_HLEN -
+ ETH_FCS_LEN;
+
+ err = register_netdev(ndev);
if (err) {
dev_err(sparx5->dev,
"port: %02u: netdev registration failed\n",
--
2.34.1