[PATCH v7 0/4] vhost-user,tcp: Handle multiple iovec entries per virtqueue element
This is the TCP counterpart to the UDP multi-iov series. It converts the TCP vhost-user receive path from direct pointer arithmetic (via vu_eth(), vu_ip(), etc.) to the iov_tail abstraction, removing the assumption that all headers reside in a single contiguous buffer. With this series applied, the TCP path correctly handles virtio-net drivers that provide multiple buffers per virtqueue element (e.g. iPXE provides the vnet header in the first buffer and the frame payload in a second one), matching the support already present in the UDP path. Based-on: 20260520095526.21519-1-lvivier@redhat.com v7: - Use initializers for struct tcphdr - Rename reused 'payload' variable to 'l2frame' - Fix vu_pad() double-offset bug in tcp_vu_data_from_sock() - Remove redundant *elem_used = 0 before truncation loop - Add missing @vnlen to tcp_vu_send_dup() doc comment v6: - Rebase on v8 of UDP series (tcp_update_csum() takes dlen rather than l4len) v5: - Use l2len variable for pcap_iov() length in tcp_vu_send_flag() - Add braces - Move pcap_iov() before vu_flush() - Remove vu_flush() from tcp_vu_send_dup(), let the caller handle it v4: - fix error during rebase, s/vu_pad_len/vu_pad/ v3: - Rebased on top of [PATCH 00/10] vhost-user: Preparatory series for multiple iovec entries per virtqueue element v2: - add "tcp: Encode checksum computation flags in a single parameter" - remove IOV_PUT_HEADER()/with_header() and use IOV_PUSH_HEADER() - don't use the iov_tail to provide the headers to the functions Laurent Vivier (4): tcp: Encode checksum computation flags in a single parameter tcp_vu: Build headers on the stack and write them into the iovec tcp_vu: Support multibuffer frames in tcp_vu_sock_recv() tcp_vu: Support multibuffer frames in tcp_vu_send_flag() iov.c | 1 - tcp.c | 25 +-- tcp_buf.c | 23 +-- tcp_internal.h | 7 +- tcp_vu.c | 409 +++++++++++++++++++++++++++++-------------------- vu_common.h | 20 --- 6 files changed, 272 insertions(+), 213 deletions(-) -- 2.54.0
tcp_fill_headers() takes a pointer to a previously computed IPv4 header
checksum to avoid recalculating it when the payload length doesn't
change, and a separate bool to skip TCP checksum computation.
Replace both parameters with a single uint32_t csum_flags that encodes:
- IP4_CSUM (bit 31): compute IPv4 header checksum from scratch
- TCP_CSUM (bit 30): compute TCP checksum
- IP4_CMASK (low 16 bits): cached IPv4 header checksum value
When IP4_CSUM is not set, the cached checksum is extracted from the low
16 bits. This is cleaner than the pointer-based approach, and also
avoids a potential dangling pointer issue: a subsequent patch makes
tcp_fill_headers() access ip4h via with_header(), which scopes it to a
temporary variable, so a pointer to ip4h->check would become invalid
after the with_header() block.
Suggested-by: David Gibson
On Wed, 20 May 2026 17:10:07 +0200
Laurent Vivier
tcp_fill_headers() takes a pointer to a previously computed IPv4 header checksum to avoid recalculating it when the payload length doesn't change, and a separate bool to skip TCP checksum computation.
Replace both parameters with a single uint32_t csum_flags that encodes: - IP4_CSUM (bit 31): compute IPv4 header checksum from scratch - TCP_CSUM (bit 30): compute TCP checksum - IP4_CMASK (low 16 bits): cached IPv4 header checksum value
Have you considered something like: struct csum_state { bool compute_tcp; bool compute_ip; uint16_t computed_ip; }; ? I think the related documentation would look simpler and it should also be more robust to future changes. Not worth a respin though, if it looks like a good idea I guess it could be done as a follow-up. -- Stefano
On 5/26/26 10:37, Stefano Brivio wrote:
On Wed, 20 May 2026 17:10:07 +0200 Laurent Vivier
wrote: tcp_fill_headers() takes a pointer to a previously computed IPv4 header checksum to avoid recalculating it when the payload length doesn't change, and a separate bool to skip TCP checksum computation.
Replace both parameters with a single uint32_t csum_flags that encodes: - IP4_CSUM (bit 31): compute IPv4 header checksum from scratch - TCP_CSUM (bit 30): compute TCP checksum - IP4_CMASK (low 16 bits): cached IPv4 header checksum value
Have you considered something like:
struct csum_state { bool compute_tcp; bool compute_ip; uint16_t computed_ip; };
? I think the related documentation would look simpler and it should also be more robust to future changes.
Not worth a respin though, if it looks like a good idea I guess it could be done as a follow-up.
Yes, I think it's a good idea. It looks cleaner. Thanks, Laurent
On Wed, 20 May 2026 17:10:06 +0200
Laurent Vivier
This is the TCP counterpart to the UDP multi-iov series. It converts the TCP vhost-user receive path from direct pointer arithmetic (via vu_eth(), vu_ip(), etc.) to the iov_tail abstraction, removing the assumption that all headers reside in a single contiguous buffer.
With this series applied, the TCP path correctly handles virtio-net drivers that provide multiple buffers per virtqueue element (e.g. iPXE provides the vnet header in the first buffer and the frame payload in a second one), matching the support already present in the UDP path.
Based-on: 20260520095526.21519-1-lvivier@redhat.com
v7: - Use initializers for struct tcphdr - Rename reused 'payload' variable to 'l2frame' - Fix vu_pad() double-offset bug in tcp_vu_data_from_sock() - Remove redundant *elem_used = 0 before truncation loop - Add missing @vnlen to tcp_vu_send_dup() doc comment
Applied. -- Stefano
participants (2)
-
Laurent Vivier
-
Stefano Brivio