[PATCH net-next 5/9] net: sparx5/lan969x: size the FDMA buffers from the port MTUs

From: Daniel Machon

Date: Thu Nov 05 2026 - 09:00:54 EST


The FDMA buffers must hold the largest frame that can be received by the
CPU, including the IFH, and on Sparx5 also the largest frame that is
transmitted, as Sparx5 copies the frames into its TX buffers. On Sparx5,
the RX buffers are skbs, and the TX buffers are a contiguous area. On
lan969x, the RX buffers are page_pool pages, which are turned into skbs
with build_skb(), and TX frames are mapped directly from the skbs. The
buffers are currently fixed to a single page, which is not large enough
to hold jumbo frames.

Instead, size the buffers from the largest MTU of the ports, plus the
Ethernet header, FCS, IFH and two VLAN tags. On Sparx5, use this length,
rounded up to the 128 byte granularity required by the FDMA, as the RX
and TX buffer size. As the TX buffers can now be smaller than a page,
make sparx5_fdma_xmit() drop frames that do not fit. On lan969x, use the
page order needed to hold the frame and the skb_shared_info, as lan966x
does, and limit the RX buffer size so that the FDMA never writes into
the skb_shared_info at the end of the page.

With the default MTU of 1500, the Sparx5 buffers become smaller, and the
lan969x pages are unchanged.

Signed-off-by: Daniel Machon <daniel.machon@xxxxxxxxxxxxx>
---
.../microchip/sparx5/lan969x/lan969x_fdma.c | 22 ++++++++++----
.../net/ethernet/microchip/sparx5/sparx5_fdma.c | 35 ++++++++++++++++++++--
.../net/ethernet/microchip/sparx5/sparx5_main.h | 2 ++
3 files changed, 52 insertions(+), 7 deletions(-)

diff --git a/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_fdma.c b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_fdma.c
index 5a6521ff774d8..26a0815a0173b 100644
--- a/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_fdma.c
+++ b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_fdma.c
@@ -131,7 +131,7 @@ static struct sk_buff *lan969x_fdma_rx_get_frame(struct sparx5 *sparx5,
if (WARN_ON(!port))
goto free_page;

- skb = build_skb(page_address(page), fdma->db_size);
+ skb = build_skb(page_address(page), PAGE_SIZE << rx->page_order);
if (unlikely(!skb))
goto free_page;

@@ -161,21 +161,27 @@ static struct sk_buff *lan969x_fdma_rx_get_frame(struct sparx5 *sparx5,
return NULL;
}

+/* Get the page order needed for a page to hold the largest frame */
+static u8 lan969x_fdma_get_page_order(struct sparx5 *sparx5)
+{
+ return get_order(ALIGN(sparx5_fdma_get_max_frame(sparx5), 128) +
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info)));
+}
+
static int lan969x_fdma_rx_alloc(struct sparx5 *sparx5, struct sparx5_rx *rx)
{
struct fdma *fdma = &rx->fdma;
int err;

struct page_pool_params pp_params = {
- .order = 0,
+ .order = rx->page_order,
.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV,
.pool_size = fdma->n_dcbs * fdma->n_dbs,
.nid = NUMA_NO_NODE,
.dev = sparx5->dev,
.dma_dir = DMA_FROM_DEVICE,
.offset = 0,
- .max_len = fdma->db_size -
- SKB_DATA_ALIGN(sizeof(struct skb_shared_info)),
+ .max_len = fdma->db_size,
};

rx->page_pool = page_pool_create(&pp_params);
@@ -238,7 +244,12 @@ static void lan969x_fdma_rx_init(struct sparx5 *sparx5, struct sparx5_rx *rx)
fdma->n_dbs = 1;
fdma->priv = sparx5;
fdma->size = fdma_get_size(fdma);
- fdma->db_size = PAGE_SIZE;
+ /* Keep the FDMA from writing into the skb_shared_info at the end of
+ * the page. The DB size must be a multiple of 128 bytes.
+ */
+ fdma->db_size = ALIGN_DOWN((PAGE_SIZE << rx->page_order) -
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info)),
+ 128);
fdma->ops.dataptr_cb = &lan969x_fdma_rx_dataptr_cb;
fdma->ops.nextptr_cb = &fdma_nextptr_cb;

@@ -383,6 +394,7 @@ int lan969x_fdma_init(struct sparx5 *sparx5)
struct sparx5_rx *rx = &sparx5->rx;
int err;

+ rx->page_order = lan969x_fdma_get_page_order(sparx5);
lan969x_fdma_rx_init(sparx5, rx);
lan969x_fdma_tx_init(sparx5);
sparx5_fdma_injection_mode(sparx5);
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c b/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c
index 7ac5887abe6ee..3a74a305d55d4 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c
@@ -18,7 +18,6 @@
#include "sparx5_main.h"
#include "sparx5_port.h"

-#define FDMA_XTR_BUFFER_SIZE 2048
#define FDMA_WEIGHT 4
#define SPX5_FDMA_STOP_SLEEP_US 500
#define SPX5_FDMA_STOP_TIMEOUT_US 10000
@@ -218,6 +217,10 @@ int sparx5_fdma_xmit(struct sparx5 *sparx5, u32 *ifh, struct sk_buff *skb,
struct fdma *fdma = &tx->fdma;
void *virt_addr;

+ /* Drop frames that do not fit in the TX buffers */
+ if (unlikely(IFH_LEN * 4 + skb->len + ETH_FCS_LEN > fdma->db_size))
+ return -EMSGSIZE;
+
if (!fdma_db_is_done(fdma_db_get(fdma, fdma->dcb_index, 0)))
return -EINVAL;

@@ -289,6 +292,33 @@ static int sparx5_fdma_tx_alloc(struct sparx5_tx *tx)
return 0;
}

+/* Get the size of the largest frame, including IFH and VLAN tags, that can be
+ * received on any of the ports.
+ */
+u32 sparx5_fdma_get_max_frame(struct sparx5 *sparx5)
+{
+ u32 max_mtu = 0;
+
+ for (int i = 0; i < sparx5->data->consts->n_ports; i++) {
+ struct sparx5_port *port = sparx5->ports[i];
+
+ if (!port)
+ continue;
+
+ max_mtu = max(max_mtu, READ_ONCE(port->ndev->mtu));
+ }
+
+ return SPX5_HW_MTU(max_mtu) + IFH_LEN * sizeof(u32) + VLAN_HLEN * 2;
+}
+
+/* Get the DB size needed to hold the largest frame. The DB size must be a
+ * multiple of 128 bytes.
+ */
+static u32 sparx5_fdma_get_db_size(struct sparx5 *sparx5)
+{
+ return ALIGN(sparx5_fdma_get_max_frame(sparx5), 128);
+}
+
static void sparx5_fdma_rx_init(struct sparx5 *sparx5,
struct sparx5_rx *rx, int channel, u32 db_size)
{
@@ -418,7 +448,7 @@ void sparx5_fdma_injection_mode(struct sparx5 *sparx5)

int sparx5_fdma_init(struct sparx5 *sparx5)
{
- u32 db_size = ALIGN(FDMA_XTR_BUFFER_SIZE, PAGE_SIZE);
+ u32 db_size;
int err;

/* Reset FDMA state */
@@ -435,6 +465,7 @@ int sparx5_fdma_init(struct sparx5 *sparx5)
sparx5, CPU_PROC_CTRL);

sparx5_fdma_injection_mode(sparx5);
+ db_size = sparx5_fdma_get_db_size(sparx5);
sparx5_fdma_rx_init(sparx5, &sparx5->rx, FDMA_XTR_CHANNEL, db_size);
sparx5_fdma_tx_init(sparx5, &sparx5->tx, FDMA_INJ_CHANNEL, db_size);
err = sparx5_fdma_rx_alloc(&sparx5->rx);
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
index 8dc3a500c1c91..18b3bd33c11a7 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
@@ -109,6 +109,7 @@ enum sparx5_feature {
#define SPX5_BUFFER_CELL_SZ 184 /* Cell size */
#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 XTR_QUEUE 0
#define INJ_QUEUE 0
@@ -479,6 +480,7 @@ int sparx5_fdma_xmit(struct sparx5 *sparx5, u32 *ifh, struct sk_buff *skb,
irqreturn_t sparx5_fdma_handler(int irq, void *args);
void sparx5_fdma_reload(struct sparx5 *sparx5, struct fdma *fdma);
void sparx5_fdma_injection_mode(struct sparx5 *sparx5);
+u32 sparx5_fdma_get_max_frame(struct sparx5 *sparx5);

/* sparx5_mactable.c */
int sparx5_mact_learn(struct sparx5 *sparx5, int port,

--
2.34.1