Skip to contentSkip to conversation

FAILED: patch "[PATCH] net: mana: reserve RX buffer headroom to fix forwarding" failed to apply to 6.18-stable tree

PATCH New [email protected] 2 participants 2 messages Oct 12 – Oct 13

Subsystems (5)
lore
Share & tools
lore

[email protected]

author maintainer
#0 lore raw

The patch below does not apply to the 6.18-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.

To reproduce the conflict and resubmit, you may use the following commands:

git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.18.y
git checkout FETCH_HEAD
git cherry-pick -x 259c4a519100e9182f264f8009ee212b32774277
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026101214-empathy-corridor-b98d@gregkh' --subject-prefix 'PATCH 6.18.y' 'HEAD^..'

Possible dependencies:

thanks,

greg k-h

------------------ original commit in Linus's tree ------------------

From 259c4a519100e9182f264f8009ee212b32774277 Mon Sep 17 00:00:00 2001
From: Hamza Mahfooz <[email protected]>
Date: Fri, 2 Oct 2026 21:36:47 -0400
Subject: [PATCH] net: mana: reserve RX buffer headroom to fix forwarding
performance

Commit 730ff06d3f5c ("net: mana: Use page pool fragments for RX buffers instead of full pages to improve memory efficiency.") started handing out RX buffers with zero headroom so that two buffers fit into one page at the default MTU.

The MANA TX path, however, stores the per scatter-gather entry DMA mappings in struct mana_skb_head at skb->head, and mana_start_xmit() therefore calls skb_cow_head(skb, MANA_HEADROOM). The port advertises this requirement as ndev->needed_headroom = MANA_HEADROOM.

As a result every packet that is received and then forwarded out of a MANA port fails the skb_cow() in ip_forward() and gets reallocated and copied by pskb_expand_head(). This is invisible to a plain RX or TX workload, but it puts a full skb reallocation plus memcpy on the hot path of every single forwarded packet, which is exactly what a router/NVA workload does.

Restore the headroom. Note that reserving MANA_HEADROOM (232) is not
enough: ip_forward() asks for LL_RESERVED_SPACE(dev), which rounds hard_header_len + needed_headroom up to HH_DATA_MOD and is 256 bytes on ethernet. Use LL_RESERVED_SPACE() directly so the value keeps tracking both constants. Also, since LL_RESERVED_SPACE() tracks MANA_HEADROOM, it grows with MAX_SKB_FRAGS and for MAX_SKB_FRAGS >= 19 it is greater than 256, so we have to account for that by using the headroom the RX queue actually uses (instead of assuming XDP_PACKET_HEADROOM) and turning MANA_XDP_MTU_MAX into MANA_XDP_MTU_MAX(ndev) (note that at the default CONFIG_MAX_SKB_FRAGS=17 they are equivalent).

At the default MTU on a 4K page this means a buffer no longer fits twice into a page (SKB_DATA_ALIGN(1500 + MANA_RXBUF_PAD + 256) = 2112), so the frag-vs-single decision is now made by computing the real buffer size instead of comparing the MTU against PAGE_SIZE / 2. The page_pool fragment path is still used wherever at least two buffers genuinely fit, e.g. on 16K and 64K page sizes.

Measured on an Azure VM with a MANA NIC acting as a forwarding NVA (UDP, 1400 byte payload, 4 streams, 8 Gbps offered, only the forwarding node's kernel differs), 8 runs each, median:

forwarded pps throughput

  before              272,830       3.06 Gbps
  after               390,560       4.37 Gbps   (+43%)

perf on the forwarding node, same workload:

memset_orig __pi_memcpy pskb_expand_head

  before             10.07%         3.96%         present
  after               0.94%         0.64%         gone
Fixes 730ff06d3f5c ("net: mana: Use page pool fragments for RX buffers instead of full pages to improve memory efficiency.")
drivers/net/ethernet/microsoft/mana/mana_bpf.c +4 -4
index 5c9961ee9747..b27fd9b8c4f6 100644
@@ -91,7 +91,7 @@ u32 mana_run_xdp(struct net_device *ndev, struct mana_rxq *rxq,
 		goto out;
 
 	xdp_init_buff(xdp, PAGE_SIZE, &rxq->xdp_rxq);
-	xdp_prepare_buff(xdp, buf_va, XDP_PACKET_HEADROOM, pkt_len, true);
+	xdp_prepare_buff(xdp, buf_va, rxq->headroom, pkt_len, true);
 
 	act = bpf_prog_run_xdp(prog, xdp);
 
@@ -183,9 +183,9 @@ static int mana_xdp_set(struct net_device *ndev, struct bpf_prog *prog,
 	if (!old_prog && !prog)
 		return 0;
 
-	if (prog && ndev->mtu > MANA_XDP_MTU_MAX) {
+	if (prog && ndev->mtu > MANA_XDP_MTU_MAX(ndev)) {
 		netdev_err(ndev, "XDP: mtu:%u too large, mtu_max:%lu\n",
-			   ndev->mtu, MANA_XDP_MTU_MAX);
+			   ndev->mtu, MANA_XDP_MTU_MAX(ndev));
 		NL_SET_ERR_MSG_MOD(extack, "XDP: mtu too large");
 
 		return -EOPNOTSUPP;
@@ -238,7 +238,7 @@ static int mana_xdp_set(struct net_device *ndev, struct bpf_prog *prog,
 		bpf_prog_put(old_prog);
 
 	if (prog)
-		ndev->max_mtu = min_t(unsigned int, MANA_XDP_MTU_MAX,
+		ndev->max_mtu = min_t(unsigned int, MANA_XDP_MTU_MAX(ndev),
 				      gc->adapter_mtu - ETH_HLEN);
 	else
 		ndev->max_mtu = gc->adapter_mtu - ETH_HLEN;
drivers/net/ethernet/microsoft/mana/mana_en.c +46 -17

95 lines not on this page · raw message

drivers/net/ethernet/microsoft/mana/mana_ethtool.c +5 -5
index ece7ff9cc409..33db79569d3b 100644
@@ -815,12 +815,12 @@ static int mana_set_priv_flags(struct net_device *ndev, u32 priv_flags)
 		if (!apc->port_is_up)
 			return 0;
 
-		/* If XDP is attached or MTU is jumbo, single-buffer-per-page
-		 * is already forced regardless of this flag. Skip the
-		 * expensive detach/attach cycle since nothing changes.
+		/* If XDP is attached or the MTU already forces one buffer per
+		 * page, single-buffer-per-page is used regardless of this
+		 * flag. Skip the expensive detach/attach cycle since nothing
+		 * changes.
 		 */
-		if (ndev->mtu + MANA_RXBUF_PAD > PAGE_SIZE / 2 ||
-		    mana_xdp_get(apc))
+		if (mana_single_rxbuf_per_page_forced(apc, ndev->mtu))
 			return 0;
 
 		/* Block RDMA from grabbing the vport during detach/attach */
include/net/mana/mana.h +13 -1
index 83b7eff4646e..8c3dc9d0b299 100644
@@ -377,7 +377,18 @@ struct mana_recv_buf_oob {
 #define MANA_RXBUF_PAD (SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) \
 			+ ETH_HLEN)
 
-#define MANA_XDP_MTU_MAX (PAGE_SIZE - MANA_RXBUF_PAD - XDP_PACKET_HEADROOM)
+/* Headroom an RX buffer has to reserve while XDP is attached: the XDP program
+ * needs XDP_PACKET_HEADROOM, and the TX path needs LL_RESERVED_SPACE() (see
+ * mana_get_rxbuf_headroom()). LL_RESERVED_SPACE() grows with MAX_SKB_FRAGS and
+ * exceeds XDP_PACKET_HEADROOM once CONFIG_MAX_SKB_FRAGS is 19 or more.
+ */
+static inline u32 mana_xdp_headroom(struct net_device *ndev)
+{
+	return max_t(u32, LL_RESERVED_SPACE(ndev), XDP_PACKET_HEADROOM);
+}
+
+#define MANA_XDP_MTU_MAX(ndev) \
+	(PAGE_SIZE - MANA_RXBUF_PAD - mana_xdp_headroom(ndev))
 
 struct mana_rxq {
 	struct gdma_queue *gdma_rq;
@@ -691,6 +702,7 @@ int mana_query_link_cfg(struct mana_port_context *apc);
 int mana_set_bw_clamp(struct mana_port_context *apc, u32 speed,
 		      int enable_clamping);
 void mana_query_phy_stats(struct mana_port_context *apc);
+bool mana_single_rxbuf_per_page_forced(struct mana_port_context *apc, u32 mtu);
 int mana_pre_alloc_rxbufs(struct mana_port_context *apc, int mtu, int num_queues);
 void mana_pre_dealloc_rxbufs(struct mana_port_context *apc);
 void mana_unmap_skb(struct sk_buff *skb, struct mana_port_context *apc);
maintainer
#1 lore raw

PATCH 1/1 net: mana: reserve RX buffer headroom to fix forwarding performance

From: Hamza Mahfooz <[email protected]>

[ Upstream commit 259c4a519100e9182f264f8009ee212b32774277 ]

Commit 730ff06d3f5c ("net: mana: Use page pool fragments for RX buffers instead of full pages to improve memory efficiency.") started handing out RX buffers with zero headroom so that two buffers fit into one page at the default MTU.

The MANA TX path, however, stores the per scatter-gather entry DMA mappings in struct mana_skb_head at skb->head, and mana_start_xmit() therefore calls skb_cow_head(skb, MANA_HEADROOM). The port advertises this requirement as ndev->needed_headroom = MANA_HEADROOM.

As a result every packet that is received and then forwarded out of a MANA port fails the skb_cow() in ip_forward() and gets reallocated and copied by pskb_expand_head(). This is invisible to a plain RX or TX workload, but it puts a full skb reallocation plus memcpy on the hot path of every single forwarded packet, which is exactly what a router/NVA workload does.

Restore the headroom. Note that reserving MANA_HEADROOM (232) is not
enough: ip_forward() asks for LL_RESERVED_SPACE(dev), which rounds hard_header_len + needed_headroom up to HH_DATA_MOD and is 256 bytes on ethernet. Use LL_RESERVED_SPACE() directly so the value keeps tracking both constants. Also, since LL_RESERVED_SPACE() tracks MANA_HEADROOM, it grows with MAX_SKB_FRAGS and for MAX_SKB_FRAGS >= 19 it is greater than 256, so we have to account for that by using the headroom the RX queue actually uses (instead of assuming XDP_PACKET_HEADROOM) and turning MANA_XDP_MTU_MAX into MANA_XDP_MTU_MAX(ndev) (note that at the default CONFIG_MAX_SKB_FRAGS=17 they are equivalent).

At the default MTU on a 4K page this means a buffer no longer fits twice into a page (SKB_DATA_ALIGN(1500 + MANA_RXBUF_PAD + 256) = 2112), so the frag-vs-single decision is now made by computing the real buffer size instead of comparing the MTU against PAGE_SIZE / 2. The page_pool fragment path is still used wherever at least two buffers genuinely fit, e.g. on 16K and 64K page sizes.

Measured on an Azure VM with a MANA NIC acting as a forwarding NVA (UDP, 1400 byte payload, 4 streams, 8 Gbps offered, only the forwarding node's kernel differs), 8 runs each, median:

forwarded pps throughput

  before              272,830       3.06 Gbps
  after               390,560       4.37 Gbps   (+43%)

perf on the forwarding node, same workload:

memset_orig __pi_memcpy pskb_expand_head

  before             10.07%         3.96%         present
  after               0.94%         0.64%         gone
Fixes 730ff06d3f5c ("net: mana: Use page pool fragments for RX buffers instead of full pages to improve memory efficiency.")
[ sashal: Kept headroom and buffer-sizing calculations in mana_get_rxbuf_cfg() because full-page RX flag support is absent. ]
---
 .../net/ethernet/microsoft/mana/mana_bpf.c    |  8 +++---
 drivers/net/ethernet/microsoft/mana/mana_en.c | 26 +++++++++----------
 include/net/mana/mana.h                       |  4 ++-
 3 files changed, 19 insertions(+), 19 deletions(-)
drivers/net/ethernet/microsoft/mana/mana_bpf.c +4 -4
index 7bb8382940b38..caf77991a54e9 100644
@@ -91,7 +91,7 @@ u32 mana_run_xdp(struct net_device *ndev, struct mana_rxq *rxq,
 		goto out;
 
 	xdp_init_buff(xdp, PAGE_SIZE, &rxq->xdp_rxq);
-	xdp_prepare_buff(xdp, buf_va, XDP_PACKET_HEADROOM, pkt_len, true);
+	xdp_prepare_buff(xdp, buf_va, rxq->headroom, pkt_len, true);
 
 	act = bpf_prog_run_xdp(prog, xdp);
 
@@ -183,9 +183,9 @@ static int mana_xdp_set(struct net_device *ndev, struct bpf_prog *prog,
 	if (!old_prog && !prog)
 		return 0;
 
-	if (prog && ndev->mtu > MANA_XDP_MTU_MAX) {
+	if (prog && ndev->mtu > MANA_XDP_MTU_MAX(ndev)) {
 		netdev_err(ndev, "XDP: mtu:%u too large, mtu_max:%lu\n",
-			   ndev->mtu, MANA_XDP_MTU_MAX);
+			   ndev->mtu, MANA_XDP_MTU_MAX(ndev));
 		NL_SET_ERR_MSG_MOD(extack, "XDP: mtu too large");
 
 		return -EOPNOTSUPP;
@@ -238,7 +238,7 @@ static int mana_xdp_set(struct net_device *ndev, struct bpf_prog *prog,
 		bpf_prog_put(old_prog);
 
 	if (prog)
-		ndev->max_mtu = MANA_XDP_MTU_MAX;
+		ndev->max_mtu = MANA_XDP_MTU_MAX(ndev);
 	else
 		ndev->max_mtu = gc->adapter_mtu - ETH_HLEN;
 
drivers/net/ethernet/microsoft/mana/mana_en.c +12 -14
index 5cbe24a1596c8..befeb20fb7d59 100644
@@ -749,7 +749,7 @@ static void *mana_get_rxbuf_pre(struct mana_rxq *rxq, dma_addr_t *da)
 	return va;
 }
 
-/* Get RX buffer's data size, alloc size, XDP headroom based on MTU */
+/* Get RX buffer's data size, alloc size, headroom and frag count based on MTU */
 static void mana_get_rxbuf_cfg(struct mana_port_context *apc,
 			       int mtu, u32 *datasize, u32 *alloc_size,
 			       u32 *headroom, u32 *frag_count)
@@ -759,16 +759,20 @@ static void mana_get_rxbuf_cfg(struct mana_port_context *apc,
 	/* Calculate datasize first (consistent across all cases) */
 	*datasize = mtu + ETH_HLEN;
 
-	/* For xdp and jumbo frames make sure only one packet fits per page */
-	if (mtu + MANA_RXBUF_PAD > PAGE_SIZE / 2 || mana_xdp_get(apc)) {
-		if (mana_xdp_get(apc)) {
-			*headroom = XDP_PACKET_HEADROOM;
+	*headroom = LL_RESERVED_SPACE(apc->ndev);
+	if (mana_xdp_get(apc))
+		*headroom = max_t(u32, *headroom, XDP_PACKET_HEADROOM);
+
+	len = SKB_DATA_ALIGN(mtu + MANA_RXBUF_PAD + *headroom);
+	buf_size = ALIGN(len, MANA_RX_FRAG_ALIGNMENT);
+
+	/* For xdp and large buffers make sure only one packet fits per page */
+	if (PAGE_SIZE / buf_size < 2 || mana_xdp_get(apc)) {
+		if (mana_xdp_get(apc))
 			*alloc_size = PAGE_SIZE;
-		} else {
-			*headroom = 0; /* no support for XDP */
+		else
 			*alloc_size = SKB_DATA_ALIGN(mtu + MANA_RXBUF_PAD +
 						     *headroom);
-		}
 
 		*frag_count = 1;
 
@@ -782,12 +786,6 @@ static void mana_get_rxbuf_cfg(struct mana_port_context *apc,
 	}
 
 	/* Standard MTU case - optimize for multiple packets per page */
-	*headroom = 0;
-
-	/* Calculate base buffer size needed */
-	len = SKB_DATA_ALIGN(mtu + MANA_RXBUF_PAD + *headroom);
-	buf_size = ALIGN(len, MANA_RX_FRAG_ALIGNMENT);
-
 	/* Calculate how many packets can fit in a page */
 	*frag_count = PAGE_SIZE / buf_size;
 	*alloc_size = buf_size;
include/net/mana/mana.h +3 -1
index a134b12352797..f03438a4b1dbd 100644
@@ -325,7 +325,9 @@ struct mana_recv_buf_oob {
 #define MANA_RXBUF_PAD (SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) \
 			+ ETH_HLEN)
 
-#define MANA_XDP_MTU_MAX (PAGE_SIZE - MANA_RXBUF_PAD - XDP_PACKET_HEADROOM)
+#define MANA_XDP_MTU_MAX(ndev) \
+	(PAGE_SIZE - MANA_RXBUF_PAD - \
+	 max_t(u32, LL_RESERVED_SPACE(ndev), XDP_PACKET_HEADROOM))
 
 struct mana_rxq {
 	struct gdma_queue *gdma_rq;
Signature · 1 line
2.53.0