Fix MANA RX with bounce buffering
Subsystems (6)
Dexuan Cui
maintainerv3 cover Fix MANA RX with bounce buffering
With swiotlb=force, the MANA NIC fails to work properly due to commit 730ff06d3f5c ("net: mana: Use page pool fragments for RX buffers instead of full pages to improve memory efficiency.").
This happens because, with the standard MTU=1500, the aforementioned commit uses page pool frags with PP_FLAG_DMA_MAP, but fails to call page_pool_dma_sync_for_cpu() to sync the received packet for CPU acces before handing the RX buffer to the stack.
Here patch #2 adds the required page_pool_dma_sync_for_cpu().
Patch #1 validates the packet length reported by the NIC. With patch #2, page_pool_dma_sync_for_cpu() uses the packet length, so we don't want to blindly trust the packet length, just in case.
There is no change between v2 and v3.
v3 just swaps the order of the 2 patches in v2, as suggested by Simon [3].
Please review.
Thanks,
Dexuan
References:
[1] v1: https://lore.kernel.org/netdev/[email protected]/
[2] v2: https://lore.kernel.org/netdev/[email protected]/
[3] https://lore.kernel.org/netdev/[email protected]/
Dexuan Cui (2):
net: mana: Validate the packet length reported by the NIC
net: mana: Sync page pool RX frags for CPU
drivers/net/ethernet/microsoft/mana/mana_en.c | 61 +++++++++++++++---- include/net/mana/mana.h | 8 +++ 2 files changed, 58 insertions(+), 11 deletions(-)
Signature · 1 line
2.34.1
Dexuan Cui
maintainerPATCH v3 1/2 net: mana: Validate the packet length reported by the NIC
Validate the packet length reported in the RX CQE before passing it to skb processing. The CQE is supplied by the NIC device and should not be blindly trusted.
---
Changes since v1:
v1 is split into two patches in the v2.
Add Haiyang's Reviewed-by.
Changes since v2:
Swapped the order of the 2 patches in v2.
No extra change.
drivers/net/ethernet/microsoft/mana/mana_en.c | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-)
drivers/net/ethernet/microsoft/mana/mana_en.c +18 -5
@@ -2170,12 +2170,25 @@ static void mana_process_rx_cqe(struct mana_rxq *rxq, struct mana_cq *cq, rxbuf_oob = &rxq->rx_oobs[curr];WARN_ON_ONCE (rxbuf_oob->wqe_inf.wqe_size_in_bu !=1 ); -mana_refill_rx_oob (dev, rxq, rxbuf_oob, &old_buf, &old_fp); +if (unlikely (pktlen > rxq->datasize)) { +/* Increase it even if mana_rx_skb() isn't called. */ + rxq->rx_cq.work_done++; -/* Unsuccessful refill will have old_buf == NULL. -* In this case, mana_rx_skb() will drop the packet. -*/ -mana_rx_skb (old_buf, old_fp, oob, rxq, i); + ++ndev->stats.rx_dropped; +netdev_warn_once (ndev, +"Dropped oversized RX packet: len=%u, datasize=%u\n" , + pktlen, rxq->datasize); + +/* Reuse the RX buffer since rxbuf_oob is unchanged. */ + }else { + +mana_refill_rx_oob (dev, rxq, rxbuf_oob, &old_buf, &old_fp); + +/* Unsuccessful refill will have old_buf == NULL. +* In this case, mana_rx_skb() will drop the packet. +*/ +mana_rx_skb (old_buf, old_fp, oob, rxq, i); + }mana_move_wq_tail (rxq->gdma_rq, rxbuf_oob->wqe_inf.wqe_size_in_bu);
Signature · 1 line
2.34.1
Paolo Abeni
maintainerRe: [PATCH net v3 1/2] net: mana: Validate the packet length reported by the NIC
Quoted 5 lines · Dexuan Cui
Validate the packet length reported in the RX CQE before passing it to skb processing. The CQE is supplied by the NIC device and should not be blindly trusted.
This need a Fixes: tag, to help stable team backport.
No need to repost: just reply here, and I'll add it while applying the patch.
Thanks,
Paolo
Dexuan Cui
maintainerRE: [EXTERNAL] Re: [PATCH net v3 1/2] net: mana: Validate the packet length reported by the NIC
Quoted 11 lines
From: Paolo Abeni <[email protected]>
Sent: Wednesday, July 8, 2026 3:57 AM
...Quoted 5 lines · Dexuan Cui
Validate the packet length reported in the RX CQE before passing it to skb processing. The CQE is supplied by the NIC device and should not be blindly trusted.
This need a Fixes: tag, to help stable team backport.
Please use:
In this first commit of the driver, mana_process_rx_cqe() only checks if pktlen is not zero, and later mana_process_rx_cqe() -> mana_rx_skb() uses the 'pkt_len' blindly.
Quoted 6 lines
No need to repost: just reply here, and I'll add it while applying the patch.
Thanks,
Paolo
Thank you, Paolo!
Thanks,
-- Dexuan
Dexuan Cui
maintainerPATCH v3 2/2 net: mana: Sync page pool RX frags for CPU
In reply to Dexuan CuiMANA allocates RX buffers from page pool fragments when frag_count is greater than 1. In that case the buffers remain DMA mapped by page pool and the RX completion path does not call dma_unmap_single(). As a result, the implicit sync-for-CPU normally performed by dma_unmap_single() is missing before the packet data is passed to the networking stack.
This breaks RX on configurations which require explicit DMA syncing, for example when booted with swiotlb=force.
Fix this by recording the page pool page and DMA sync offset when the RX buffer is allocated, and syncing the received packet range for CPU access before handing the RX buffer to the stack.
---
Changes since v1:
v1 is split into two patches in the v2.
Add Haiyang's Reviewed-by.
Changes since v2:
Swapped the order of the 2 patches in v2.
No extra change.
drivers/net/ethernet/microsoft/mana/mana_en.c | 40 +++++++++++++++---- include/net/mana/mana.h | 8 ++++ 2 files changed, 41 insertions(+), 7 deletions(-)
drivers/net/ethernet/microsoft/mana/mana_en.c +33 -7
@@ -2044,12 +2044,16 @@ static void mana_rx_skb(void *buf_va, bool from_pool, }static void *mana_get_rxfrag (struct mana_rxq *rxq,struct device *dev, -dma_addr_t *da,bool *from_pool) +dma_addr_t *da,bool *from_pool, +struct page **pp_page,u32 *dma_sync_offset) {struct page *page;u32 offset;void *va; + *from_pool = false; + *pp_page =NULL ; + *dma_sync_offset =0 ;/* Don't use fragments for jumbo frames or XDP where it's 1 fragment * per page.
@@ -2087,31 +2091,47 @@ static void *mana_get_rxfrag(struct mana_rxq *rxq, struct device *dev, va =page_to_virt (page) + offset; *da =page_pool_get_dma_addr (page) + offset + rxq->headroom; *from_pool = true; + *pp_page = page; + *dma_sync_offset = offset + rxq->headroom;return va; }/* Allocate frag for rx buffer, and save the old buf */ static void mana_refill_rx_oob (struct device *dev,struct mana_rxq *rxq, -struct mana_recv_buf_oob *rxoob,void **old_buf, -bool *old_fp) +struct mana_recv_buf_oob *rxoob,u32 pktlen, +void **old_buf,bool *old_fp) { +struct page *pp_page; +u32 dma_sync_offset;bool from_pool;dma_addr_t da;void *va; - va =mana_get_rxfrag (rxq, dev, &da, &from_pool); + va =mana_get_rxfrag (rxq, dev, &da, &from_pool, &pp_page, + &dma_sync_offset);if (!va)return ; -if (!rxoob->from_pool || rxq->frag_count ==1 ) +if (!rxoob->from_pool || rxq->frag_count ==1 ) {dma_unmap_single (dev, rxoob->sgl[0 ].address, rxq->datasize,DMA_FROM_DEVICE ); + }else { +/* The page pool maps the whole page and only syncs for device +* automatically (PP_FLAG_DMA_SYNC_DEV). Sync the received bytes +* for the CPU before they are read: this is required if DMA +* is incoherent or bounce buffers are used. +*/ +page_pool_dma_sync_for_cpu (rxq->page_pool, rxoob->pp_page, + rxoob->dma_sync_offset, pktlen); + } *old_buf = rxoob->buf_va; *old_fp = rxoob->from_pool; rxoob->buf_va = va; rxoob->sgl[0 ].address = da; rxoob->from_pool = from_pool; + rxoob->pp_page = pp_page; + rxoob->dma_sync_offset = dma_sync_offset; } static void mana_process_rx_cqe(struct mana_rxq *rxq, struct mana_cq *cq,
@@ -2182,7 +2202,8 @@ static void mana_process_rx_cqe(struct mana_rxq *rxq, struct mana_cq *cq,/* Reuse the RX buffer since rxbuf_oob is unchanged. */ }else { -mana_refill_rx_oob (dev, rxq, rxbuf_oob, &old_buf, &old_fp); +mana_refill_rx_oob (dev, rxq, rxbuf_oob, pktlen, + &old_buf, &old_fp);/* Unsuccessful refill will have old_buf == NULL. * In this case, mana_rx_skb() will drop the packet.
@@ -2579,6 +2600,8 @@ static int mana_fill_rx_oob(struct mana_recv_buf_oob *rx_oob, u32 mem_key, struct mana_rxq *rxq, struct device *dev) { struct mana_port_context *mpc = netdev_priv(rxq->ndev); +struct page *pp_page =NULL ; +u32 dma_sync_offset =0 ;bool from_pool = false;dma_addr_t da;void *va;
@@ -2586,13 +2609,16 @@ static int mana_fill_rx_oob(struct mana_recv_buf_oob *rx_oob, u32 mem_key,if (mpc->rxbufs_pre) va =mana_get_rxbuf_pre (rxq, &da);else - va =mana_get_rxfrag (rxq, dev, &da, &from_pool); + va =mana_get_rxfrag (rxq, dev, &da, &from_pool, &pp_page, + &dma_sync_offset);if (!va)return -ENOMEM ; rx_oob->buf_va = va; rx_oob->from_pool = from_pool; + rx_oob->pp_page = pp_page; + rx_oob->dma_sync_offset = dma_sync_offset; rx_oob->sgl[0 ].address = da; rx_oob->sgl[0 ].size = rxq->datasize;
include/net/mana/mana.h +8 -0
@@ -305,6 +305,14 @@ struct mana_recv_buf_oob {void *buf_va;bool from_pool;/* allocated from a page pool */ +/* head page of the page_pool fragment; valid only when +* from_pool && frag_count > 1. +*/ +struct page *pp_page; +/* Fragment offset plus rxq->headroom, passed to +* page_pool_dma_sync_for_cpu(). +*/ +u32 dma_sync_offset;/* SGL of the buffer going to be sent as part of the work request. */ u32 num_sge;
Signature · 1 line
2.34.1
This series was applied to netdev/net.git (main) by Paolo Abeni <[email protected]>:
Hello:
This series was applied to netdev/net.git (main)
by Paolo Abeni <[email protected]>:
Quoted 10 lines · On Wed, 1 Jul 2026 21:12:35 -0700 you wrote:
With swiotlb=force, the MANA NIC fails to work properly due to commit
730ff06d3f5c("net: mana: Use page pool fragments for RX buffers instead of full pages to improve memory efficiency.").This happens because, with the standard MTU=1500, the aforementioned commit uses page pool frags with
PP_FLAG_DMA_MAP, but fails to callpage_pool_dma_sync_for_cpu()to sync the received packet for CPU acces before handing the RX buffer to the stack.[...]
Here is the summary with links:
- [net,v3,1/2] net: mana: Validate the packet length reported by the NIC
https://git.kernel.org/netdev/net/c/2e2a83b4998a - [net,v3,2/2] net: mana: Sync page pool RX frags for CPU https://git.kernel.org/netdev/net/c/c72a0f09c57f
You are awesome, thank you!
Signature · 2 lines
Deet-doot-dot, I am a bot. https://korg.docs.kernel.org/patchwork/pwbot.html