[PATCH net-next v8 12/15] net: lan966x: add PCIe FDMA MTU change support
flat view
COLD17d
SUPERSEDED: 16 (16M)
From: Daniel Machon <daniel.machon@microchip.com>
Date: 2026-09-24 19:57:59
Also in:
bpf, linux-arm-kernel, lkml
Subsystem:
microchip lan966x ethernet driver, networking drivers, the rest · Maintainers:
Horatiu Vultur, Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
Revision v8 of 8 in this series; 2 review trailers.
Revisions (8)
Add MTU change support for the PCIe FDMA path: on an MTU change, the contiguous ATU-mapped RX and TX buffers are reallocated at the new size, falling back to the existing buffers on failure. Cap the PCIe DCB ring at 256 (FDMA_PCI_DCB_MAX): 512 DCBs would overflow MAX_PAGE_ORDER at jumbo MTU. The ring must fit one MAX_PAGE_ORDER block after ATU padding, and db_size is handed to the FDMA in the 16-bit DCB DATAL field. Advertise the resulting limit in dev->max_mtu (FDMA_PCI_MAX_MTU) when the FDMA is in use - a switch with no "fdma" interrupt named uses the unconstrained register-based path instead, so the cap is gated on lan966x->fdma, not lan966x_is_pci() alone. On a 4KB-page, MAX_PAGE_ORDER=10 build this is MTU 15498; the overhead is shared with lan966x_fdma_get_max_frame() via FDMA_OVERHEAD. Skip the resize until lan966x_fdma_pci_init() has built the rings; it runs after the netdevs register and sizes them from DEV_MAC_MAXLEN_CFG, already programmed by the caller. Tested-by: Herve Codina <herve.codina@bootlin.com> Signed-off-by: Daniel Machon <daniel.machon@microchip.com> Reviewed-by: Simon Horman <horms@kernel.org> --- .../net/ethernet/microchip/lan966x/lan966x_fdma.c | 6 +- .../ethernet/microchip/lan966x/lan966x_fdma_pci.c | 153 ++++++++++++++++++++- .../net/ethernet/microchip/lan966x/lan966x_main.c | 3 +- .../net/ethernet/microchip/lan966x/lan966x_main.h | 28 ++++ 4 files changed, 181 insertions(+), 9 deletions(-)
diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c
index 2e8f786d6fee..a7940eca5df3 100644
--- a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c@@ -890,11 +890,7 @@ static int lan966x_fdma_reload(struct lan966x *lan966x, int new_mtu) int lan966x_fdma_get_max_frame(struct lan966x *lan966x) { - return lan966x_fdma_get_max_mtu(lan966x) + - IFH_LEN_BYTES + - SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + - VLAN_HLEN * 2 + - XDP_PACKET_HEADROOM; + return lan966x_fdma_get_max_mtu(lan966x) + FDMA_OVERHEAD; } static int __lan966x_fdma_reload(struct lan966x *lan966x, int max_mtu)
diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma_pci.c b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma_pci.c
index bccd1b8590d7..7185e65dda43 100644
--- a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma_pci.c
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma_pci.c@@ -358,7 +358,7 @@ static int lan966x_fdma_pci_init(struct lan966x *lan966x) lan966x->rx.lan966x = lan966x; lan966x->rx.max_mtu = lan966x_fdma_get_max_frame(lan966x); rx_fdma->channel_id = FDMA_XTR_CHANNEL; - rx_fdma->n_dcbs = FDMA_DCB_MAX; + rx_fdma->n_dcbs = FDMA_PCI_DCB_MAX; rx_fdma->n_dbs = FDMA_RX_DCB_MAX_DBS; rx_fdma->priv = lan966x; rx_fdma->db_size = FDMA_PCI_DB_SIZE(lan966x->rx.max_mtu);
@@ -368,7 +368,7 @@ static int lan966x_fdma_pci_init(struct lan966x *lan966x) lan966x->tx.lan966x = lan966x; tx_fdma->channel_id = FDMA_INJ_CHANNEL; - tx_fdma->n_dcbs = FDMA_DCB_MAX; + tx_fdma->n_dcbs = FDMA_PCI_DCB_MAX; tx_fdma->n_dbs = FDMA_TX_DCB_MAX_DBS; tx_fdma->priv = lan966x; tx_fdma->db_size = FDMA_PCI_DB_SIZE(lan966x->rx.max_mtu);
@@ -391,9 +391,156 @@ static int lan966x_fdma_pci_init(struct lan966x *lan966x) return 0; } +/* Reset existing rx and tx buffers. */ +static void lan966x_fdma_pci_reset_mem(struct lan966x *lan966x) +{ + struct lan966x_rx *rx = &lan966x->rx; + struct lan966x_tx *tx = &lan966x->tx; + + memset(rx->fdma.dcbs, 0, rx->fdma.size); + memset(tx->fdma.dcbs, 0, tx->fdma.size); + + fdma_dcbs_init(&rx->fdma, + FDMA_DCB_INFO_DATAL(rx->fdma.db_size - XDP_PACKET_HEADROOM), + FDMA_DCB_STATUS_INTR); + + fdma_dcbs_init(&tx->fdma, + FDMA_DCB_INFO_DATAL(tx->fdma.db_size), + FDMA_DCB_STATUS_DONE); + + lan966x_fdma_llp_configure(lan966x, + tx->fdma.atu_region->base_addr, + tx->fdma.channel_id); + lan966x_fdma_llp_configure(lan966x, + rx->fdma.atu_region->base_addr, + rx->fdma.channel_id); +} + +/* Wake all TX queues on every port (undoes lan966x_fdma_tx_disable_netdev). */ +static void lan966x_fdma_pci_wakeup_netdev(struct lan966x *lan966x) +{ + for (int i = 0; i < lan966x->num_phys_ports; ++i) { + struct lan966x_port *port = lan966x->ports[i]; + + if (port) + netif_tx_wake_all_queues(port->dev); + } +} + +static int lan966x_fdma_pci_reload(struct lan966x *lan966x, int new_mtu) +{ + struct fdma tx_fdma_old = lan966x->tx.fdma; + struct fdma rx_fdma_old = lan966x->rx.fdma; + u32 old_mtu = lan966x->rx.max_mtu; + int err; + + napi_disable(&lan966x->napi); + lan966x_fdma_tx_disable_netdev(lan966x); + lan966x_fdma_rx_disable(&lan966x->rx); + lan966x_fdma_tx_disable(&lan966x->tx); + + lan966x->rx.max_mtu = new_mtu; + + /* Must be NULL'ed in order to realloc them. */ + lan966x->rx.fdma.atu_region = NULL; + lan966x->tx.fdma.atu_region = NULL; + + lan966x->tx.fdma.db_size = FDMA_PCI_DB_SIZE(lan966x->rx.max_mtu); + lan966x->tx.fdma.size = fdma_get_size_contiguous(&lan966x->tx.fdma); + lan966x->rx.fdma.db_size = FDMA_PCI_DB_SIZE(lan966x->rx.max_mtu); + lan966x->rx.fdma.size = fdma_get_size_contiguous(&lan966x->rx.fdma); + + err = lan966x_fdma_pci_rx_alloc(&lan966x->rx); + if (err) + goto restore; + + err = lan966x_fdma_pci_tx_alloc(&lan966x->tx); + if (err) { + fdma_free_coherent_and_unmap(lan966x->dma_dev, + &lan966x->rx.fdma); + goto restore; + } + + /* Free and unmap old memory. */ + fdma_free_coherent_and_unmap(lan966x->dma_dev, &rx_fdma_old); + fdma_free_coherent_and_unmap(lan966x->dma_dev, &tx_fdma_old); + + /* Order matters: napi_enable() must precede the wakes, or a TX that + * completes first clears FDMA_INTR_DB_ENA with nothing scheduled to + * restore it, leaving RX dead until the next reload. + */ + napi_enable(&lan966x->napi); + lan966x_fdma_rx_start(&lan966x->rx); + lan966x_fdma_pci_wakeup_netdev(lan966x); + + return err; +restore: + + /* No new buffers are allocated at this point. Use the old buffers, + * but reset them before starting the FDMA again. + */ + + memcpy(&lan966x->tx.fdma, &tx_fdma_old, sizeof(struct fdma)); + memcpy(&lan966x->rx.fdma, &rx_fdma_old, sizeof(struct fdma)); + + lan966x->rx.max_mtu = old_mtu; + + lan966x_fdma_pci_reset_mem(lan966x); + + napi_enable(&lan966x->napi); + lan966x_fdma_rx_start(&lan966x->rx); + lan966x_fdma_pci_wakeup_netdev(lan966x); + + return err; +} + +static int __lan966x_fdma_pci_reload(struct lan966x *lan966x, int max_mtu) +{ + int err; + u32 val; + + /* Disable the CPU port. */ + lan_rmw(QSYS_SW_PORT_MODE_PORT_ENA_SET(0), + QSYS_SW_PORT_MODE_PORT_ENA, + lan966x, QSYS_SW_PORT_MODE(CPU_PORT)); + + /* Flush the CPU queues. */ + readx_poll_timeout(lan966x_qsys_sw_status, + lan966x, + val, + !(QSYS_SW_STATUS_EQ_AVAIL_GET(val)), + READL_SLEEP_US, READL_TIMEOUT_US); + + /* Add a sleep in case there are frames between the queues and the CPU + * port + */ + usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC); + + err = lan966x_fdma_pci_reload(lan966x, max_mtu); + + /* Enable back the CPU port. */ + lan_rmw(QSYS_SW_PORT_MODE_PORT_ENA_SET(1), + QSYS_SW_PORT_MODE_PORT_ENA, + lan966x, QSYS_SW_PORT_MODE(CPU_PORT)); + + return err; +} + static int lan966x_fdma_pci_resize(struct lan966x *lan966x) { - return -EOPNOTSUPP; + int max_mtu; + + /* Nothing to resize until fdma_pci_init() has built the rings; it + * sizes them from DEV_MAC_MAXLEN_CFG, which the caller already set. + */ + if (!lan966x->rx.lan966x) + return 0; + + max_mtu = lan966x_fdma_get_max_frame(lan966x); + if (max_mtu == lan966x->rx.max_mtu) + return 0; + + return __lan966x_fdma_pci_reload(lan966x, max_mtu); } static void lan966x_fdma_pci_deinit(struct lan966x *lan966x)
diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c
index de2202786826..c3afc4cc597f 100644
--- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c@@ -823,7 +823,8 @@ static int lan966x_probe_port(struct lan966x *lan966x, u32 p, port->chip_port = p; lan966x->ports[p] = port; - dev->max_mtu = ETH_MAX_MTU; + dev->max_mtu = lan966x_is_pci(lan966x) && lan966x->fdma ? + FDMA_PCI_MAX_MTU : ETH_MAX_MTU; dev->netdev_ops = &lan966x_port_netdev_ops; dev->ethtool_ops = &lan966x_ethtool_ops;
diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h
index 16bc28c8f11f..1877f1916d71 100644
--- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h@@ -7,6 +7,7 @@ #include <linux/etherdevice.h> #include <linux/if_vlan.h> #include <linux/jiffies.h> +#include <linux/mmzone.h> #include <linux/phy.h> #include <linux/phylink.h> #include <linux/ptp_clock_kernel.h>
@@ -87,6 +88,33 @@ #define FDMA_INJ_CHANNEL 0 #define FDMA_DCB_MAX 512 +/* Ring must fit in one MAX_PAGE_ORDER DMA block; 512 DCBs overflows + * at jumbo MTU. + */ +#define FDMA_PCI_DCB_MAX 256 + +#define FDMA_OVERHEAD \ + (IFH_LEN_BYTES + \ + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + \ + VLAN_HLEN * 2 + \ + XDP_PACKET_HEADROOM) + +/* Largest db_size keeping the ATU-padded ring inside one MAX_PAGE_ORDER + * block and within the 16-bit DCB DATAL field. Inverts ALIGN(x, R) <= L + * into x <= ALIGN_DOWN(L, R) to bound x directly. + */ +#define FDMA_PCI_DB_SIZE_MAX \ + MIN_T(u32, \ + (ALIGN_DOWN(PAGE_SIZE << MAX_PAGE_ORDER, \ + FDMA_PCI_ATU_REGION_ALIGN) - \ + FDMA_PCI_DCB_MAX * sizeof(struct fdma_dcb)) / \ + (FDMA_PCI_DCB_MAX * FDMA_RX_DCB_MAX_DBS), \ + ALIGN_DOWN(GENMASK(15, 0), FDMA_PCI_DB_ALIGN)) + +#define FDMA_PCI_MAX_MTU \ + (FDMA_PCI_DB_SIZE_MAX - FDMA_OVERHEAD - \ + (ETH_HLEN + ETH_FCS_LEN)) + #define SE_IDX_QUEUE 0 /* 0-79 : Queue scheduler elements */ #define SE_IDX_PORT 80 /* 80-89 : Port schedular elements */
--
2.34.1