Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

virtio_net: sync RX buffer before reading the header

receive_buf() reads the virtio header through buf before
page_pool_dma_sync_for_cpu() runs in receive_small() or
receive_mergeable(). The header buffer is thus unsynchronized at the
point where flags and, for mergeable buffers, num_buffers are consumed.

Omar Elghoul reported that on s390x Secure Execution this showed up as
greatly reduced virtio-net performance together with "bad gso" and
"bad csum" messages in dmesg. This is because with SE sync actually
copies data, so the header is uninitialized.

Move the sync into receive_buf() so the
header is synchronized before any access through buf.

Tool use: Cursor with GPT-5.4 drafted the initial code move from prompt:
"in drivers/net/virtio_net.c, move page_pool_dma_sync_for_cpu on receive
path to before memory is accessed through buf".
The result and the commit log were reviewed and edited manually.

Fixes: 24fbd3967f3f ("virtio_net: add page_pool support for buffer allocation")
Reported-by: Omar Elghoul <oelghoul@linux.ibm.com>
Tested-by: Srikanth Aithal <sraithal@amd.com>
Tested-by: Omar Elghoul <oelghoul@linux.ibm.com>
Link: https://lore.kernel.org/r/20260323150136.14452-1-oelghoul@linux.ibm.com
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Tested-by: Vishwanath Seshagiri <vishs@meta.com>
Acked-by: Jason Wang <jasowang@redhat.com>
Link: https://patch.msgid.link/f4caa9be9e5addae7851c012cab0a733be7f0974.1774365273.git.mst@redhat.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Michael S. Tsirkin and committed by
Jakub Kicinski
fe3e5425 a771cddb

+10 -10
+10 -10
drivers/net/virtio_net.c
··· 1956 1956 */ 1957 1957 buf -= VIRTNET_RX_PAD + xdp_headroom; 1958 1958 1959 - if (rq->use_page_pool_dma) { 1960 - int offset = buf - page_address(page) + 1961 - VIRTNET_RX_PAD + xdp_headroom; 1962 - 1963 - page_pool_dma_sync_for_cpu(rq->page_pool, page, offset, len); 1964 - } 1965 - 1966 1959 len -= vi->hdr_len; 1967 1960 u64_stats_add(&stats->bytes, len); 1968 1961 ··· 2391 2398 2392 2399 head_skb = NULL; 2393 2400 2394 - if (rq->use_page_pool_dma) 2395 - page_pool_dma_sync_for_cpu(rq->page_pool, page, offset, len); 2396 - 2397 2401 u64_stats_add(&stats->bytes, len - vi->hdr_len); 2398 2402 2399 2403 if (check_mergeable_len(dev, ctx, len)) ··· 2551 2561 DEV_STATS_INC(dev, rx_length_errors); 2552 2562 virtnet_rq_free_buf(vi, rq, buf); 2553 2563 return; 2564 + } 2565 + 2566 + /* Sync the memory before touching anything through buf, 2567 + * unless virtio core did it already. 2568 + */ 2569 + if (rq->use_page_pool_dma) { 2570 + struct page *page = virt_to_head_page(buf); 2571 + int offset = buf - page_address(page); 2572 + 2573 + page_pool_dma_sync_for_cpu(rq->page_pool, page, offset, len); 2554 2574 } 2555 2575 2556 2576 /* About the flags below: