FAILED: patch "[PATCH] net: mana: reserve RX buffer headroom to fix forwarding" failed to apply to 7.2-stable tree
Subsystems (5)
[email protected]
maintainerThe patch below does not apply to the 7.2-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-7.2.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 '2026101213-empirical-glancing-002d@gregkh' --subject-prefix 'PATCH 7.2.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
drivers/net/ethernet/microsoft/mana/mana_bpf.c +4 -4
@@ -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
@@ -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
@@ -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);
Sasha Levin
maintainerPATCH 1/2 net: mana: refactor mana_get_strings() and mana_get_sset_count() to use switch
From: Dipayaan Roy <[email protected]>
[ Upstream commit d52f80731055ee43b36bf60088679aca97a484da ]
Refactor mana_get_strings() and mana_get_sset_count() from if/else to switch statements in preparation for adding ethtool private flags support which requires handling ETH_SS_PRIV_FLAGS.
No functional change.
Stable-dep-of: 259c4a519100 ("net: mana: reserve RX buffer headroom to fix forwarding performance")
--- .../ethernet/microsoft/mana/mana_ethtool.c | 75 ++++++++++++------- 1 file changed, 46 insertions(+), 29 deletions(-)
drivers/net/ethernet/microsoft/mana/mana_ethtool.c +46 -29
@@ -138,53 +138,70 @@ static int mana_get_sset_count(struct net_device *ndev, int stringset)struct mana_port_context *apc =netdev_priv (ndev);unsigned int num_queues = apc->num_queues; -if (stringset !=ETH_SS_STATS ) +switch (stringset) { +case ETH_SS_STATS : +return ARRAY_SIZE (mana_eth_stats) + +ARRAY_SIZE (mana_phy_stats) + +ARRAY_SIZE (mana_hc_stats) + + num_queues * (MANA_STATS_RX_COUNT +MANA_STATS_TX_COUNT ); +default :return -EINVAL ; - -return ARRAY_SIZE (mana_eth_stats) +ARRAY_SIZE (mana_phy_stats) +ARRAY_SIZE (mana_hc_stats) + - num_queues * (MANA_STATS_RX_COUNT +MANA_STATS_TX_COUNT ); + } } -static void mana_get_strings (struct net_device *ndev,u32 stringset,u8 *data) +static void mana_get_strings_stats (struct mana_port_context *apc,u8 **data) { -struct mana_port_context *apc =netdev_priv (ndev);unsigned int num_queues = apc->num_queues;int i, j; -if (stringset !=ETH_SS_STATS ) -return ;for (i =0 ; i <ARRAY_SIZE (mana_eth_stats); i++) -ethtool_puts (&data, mana_eth_stats[i].name); +ethtool_puts (data, mana_eth_stats[i].name);for (i =0 ; i <ARRAY_SIZE (mana_hc_stats); i++) -ethtool_puts (&data, mana_hc_stats[i].name); +ethtool_puts (data, mana_hc_stats[i].name);for (i =0 ; i <ARRAY_SIZE (mana_phy_stats); i++) -ethtool_puts (&data, mana_phy_stats[i].name); +ethtool_puts (data, mana_phy_stats[i].name);for (i =0 ; i < num_queues; i++) { -ethtool_sprintf (&data,"rx_%d_packets" , i); -ethtool_sprintf (&data,"rx_%d_bytes" , i); -ethtool_sprintf (&data,"rx_%d_xdp_drop" , i); -ethtool_sprintf (&data,"rx_%d_xdp_tx" , i); -ethtool_sprintf (&data,"rx_%d_xdp_redirect" , i); -ethtool_sprintf (&data,"rx_%d_pkt_len0_err" , i); +ethtool_sprintf (data,"rx_%d_packets" , i); +ethtool_sprintf (data,"rx_%d_bytes" , i); +ethtool_sprintf (data,"rx_%d_xdp_drop" , i); +ethtool_sprintf (data,"rx_%d_xdp_tx" , i); +ethtool_sprintf (data,"rx_%d_xdp_redirect" , i); +ethtool_sprintf (data,"rx_%d_pkt_len0_err" , i);for (j =0 ; j <MANA_RXCOMP_OOB_NUM_PPI -1 ; j++) -ethtool_sprintf (&data,"rx_%d_coalesced_cqe_%d" , i, j +2 ); +ethtool_sprintf (data, +"rx_%d_coalesced_cqe_%d" , + i, + j +2 ); }for (i =0 ; i < num_queues; i++) { -ethtool_sprintf (&data,"tx_%d_packets" , i); -ethtool_sprintf (&data,"tx_%d_bytes" , i); -ethtool_sprintf (&data,"tx_%d_xdp_xmit" , i); -ethtool_sprintf (&data,"tx_%d_tso_packets" , i); -ethtool_sprintf (&data,"tx_%d_tso_bytes" , i); -ethtool_sprintf (&data,"tx_%d_tso_inner_packets" , i); -ethtool_sprintf (&data,"tx_%d_tso_inner_bytes" , i); -ethtool_sprintf (&data,"tx_%d_long_pkt_fmt" , i); -ethtool_sprintf (&data,"tx_%d_short_pkt_fmt" , i); -ethtool_sprintf (&data,"tx_%d_csum_partial" , i); -ethtool_sprintf (&data,"tx_%d_mana_map_err" , i); +ethtool_sprintf (data,"tx_%d_packets" , i); +ethtool_sprintf (data,"tx_%d_bytes" , i); +ethtool_sprintf (data,"tx_%d_xdp_xmit" , i); +ethtool_sprintf (data,"tx_%d_tso_packets" , i); +ethtool_sprintf (data,"tx_%d_tso_bytes" , i); +ethtool_sprintf (data,"tx_%d_tso_inner_packets" , i); +ethtool_sprintf (data,"tx_%d_tso_inner_bytes" , i); +ethtool_sprintf (data,"tx_%d_long_pkt_fmt" , i); +ethtool_sprintf (data,"tx_%d_short_pkt_fmt" , i); +ethtool_sprintf (data,"tx_%d_csum_partial" , i); +ethtool_sprintf (data,"tx_%d_mana_map_err" , i); + } +} + +static void mana_get_strings (struct net_device *ndev,u32 stringset,u8 *data) +{ +struct mana_port_context *apc =netdev_priv (ndev); + +switch (stringset) { +case ETH_SS_STATS : +mana_get_strings_stats (apc, &data); +break ; +default : +break ; } }
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/2 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
--- .../net/ethernet/microsoft/mana/mana_bpf.c | 8 +-- drivers/net/ethernet/microsoft/mana/mana_en.c | 60 ++++++++++++++----- include/net/mana/mana.h | 14 ++++- 3 files changed, 63 insertions(+), 19 deletions(-)
drivers/net/ethernet/microsoft/mana/mana_bpf.c +4 -4
@@ -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 -14
83 lines not on this page · raw message
include/net/mana/mana.h +13 -1
@@ -328,7 +328,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;
@@ -625,6 +636,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);
Signature · 1 line
2.53.0