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 --- .../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 9bd3f7827b1f2..03643adad4caf 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