From: David Gibson <david@gibson.dropbear.id.au>
To: Laurent Vivier <lvivier@redhat.com>
Cc: passt-dev@passt.top
Subject: Re: [PATCH v2 08/13] udp_vu: Use iov_tail in udp_vu_prepare()
Date: Thu, 12 Mar 2026 15:30:14 +1100 [thread overview]
Message-ID: <abJBVhDtr1vGOndC@zatzit> (raw)
In-Reply-To: <20260309094744.1907754-9-lvivier@redhat.com>
[-- Attachment #1: Type: text/plain, Size: 6449 bytes --]
On Mon, Mar 09, 2026 at 10:47:39AM +0100, Laurent Vivier wrote:
> Rework udp_vu_prepare() to use IOV_REMOVE_HEADER() and IOV_PUT_HEADER()
> to walk through Ethernet, IP and UDP headers instead of the layout-specific
> helpers (vu_eth(), vu_ip(), vu_payloadv4(), vu_payloadv6()) that assume a
> contiguous buffer. The payload length is now implicit in the iov_tail, so
> drop the dlen parameter.
>
> Signed-off-by: Laurent Vivier <lvivier@redhat.com>
> ---
> iov.c | 1 -
> udp_vu.c | 64 ++++++++++++++++++++++++++++++--------------------------
> 2 files changed, 34 insertions(+), 31 deletions(-)
>
> diff --git a/iov.c b/iov.c
> index 296f24b61067..1f554f5ac297 100644
> --- a/iov.c
> +++ b/iov.c
> @@ -313,7 +313,6 @@ void *iov_peek_header_(struct iov_tail *tail, void *v, size_t len, size_t align)
> *
> * Return: number of bytes written
> */
> -/* cppcheck-suppress unusedFunction */
> size_t iov_put_header_(struct iov_tail *tail, const void *v, size_t len)
> {
> size_t l = len;
> diff --git a/udp_vu.c b/udp_vu.c
> index 2a5d3f822bf6..a21a03dbf23e 100644
> --- a/udp_vu.c
> +++ b/udp_vu.c
> @@ -101,52 +101,54 @@ static ssize_t udp_vu_sock_recv(struct iovec *iov, size_t *cnt, int s, bool v6)
> * @c: Execution context
> * @data: IO vector tail for the frame
> * @toside: Address information for one side of the flow
> - * @dlen: Packet data length
> *
> * Return: Layer-4 length
> */
> static size_t udp_vu_prepare(const struct ctx *c, const struct iov_tail *data,
> - const struct flowside *toside, ssize_t dlen)
> + const struct flowside *toside)
> {
> - const struct iovec *iov = data->iov;
> - struct ethhdr *eh;
> + struct iov_tail current = *data;
> + struct ethhdr *eh, eh_storage;
> + struct udphdr *uh, uh_storage;
> size_t l4len;
>
> /* ethernet header */
> - eh = vu_eth(iov[0].iov_base);
> + eh = IOV_REMOVE_HEADER(¤t, eh_storage);
>
> memcpy(eh->h_dest, c->guest_mac, sizeof(eh->h_dest));
> memcpy(eh->h_source, c->our_tap_mac, sizeof(eh->h_source));
>
> /* initialize header */
> if (inany_v4(&toside->eaddr) && inany_v4(&toside->oaddr)) {
> - struct iphdr *iph = vu_ip(iov[0].iov_base);
> - struct udp_payload_t *bp = vu_payloadv4(iov[0].iov_base);
> - const struct iovec payload_iov = {
> - .iov_base = bp->data,
> - .iov_len = dlen,
> - };
> - struct iov_tail payload = IOV_TAIL(&payload_iov, 1, 0);
> + struct iphdr *iph, iph_storage;
>
> eh->h_proto = htons(ETH_P_IP);
>
> + iph = IOV_REMOVE_HEADER(¤t, iph_storage);
> *iph = (struct iphdr)L2_BUF_IP4_INIT(IPPROTO_UDP);
>
> - l4len = udp_update_hdr4(iph, &bp->uh, &payload, toside, true);
> + uh = IOV_REMOVE_HEADER(¤t, uh_storage);
> + l4len = udp_update_hdr4(iph, uh, ¤t, toside, true);
> +
> + current = *data;
Oof, having to reset the tail to the original position in order to
replay the PUTs in the right order seems kind of fragile. I'm
beginning to wonder if for writing (not reading) headers, trying to
use an in-place pointer is more trouble than it's worth. It would
certainly be easier to reason about this, if you always construct the
header in an external buffer, then there's just a sequence of
IOV_PUT_HEADER()s to stack them into the iov tail.
> + IOV_PUT_HEADER(¤t, eh);
> + IOV_PUT_HEADER(¤t, iph);
> + IOV_PUT_HEADER(¤t, uh);
> } else {
> - struct ipv6hdr *ip6h = vu_ip(iov[0].iov_base);
> - struct udp_payload_t *bp = vu_payloadv6(iov[0].iov_base);
> - const struct iovec payload_iov = {
> - .iov_base = bp->data,
> - .iov_len = dlen,
> - };
> - struct iov_tail payload = IOV_TAIL(&payload_iov, 1, 0);
> + struct ipv6hdr *ip6h, ip6h_storage;
>
> eh->h_proto = htons(ETH_P_IPV6);
>
> + ip6h = IOV_REMOVE_HEADER(¤t, ip6h_storage);
> *ip6h = (struct ipv6hdr)L2_BUF_IP6_INIT(IPPROTO_UDP);
>
> - l4len = udp_update_hdr6(ip6h, &bp->uh, &payload, toside, true);
> + uh = IOV_REMOVE_HEADER(¤t, uh_storage);
> + l4len = udp_update_hdr6(ip6h, uh, ¤t, toside, true);
> +
> + current = *data;
> + IOV_PUT_HEADER(¤t, eh);
> + IOV_PUT_HEADER(¤t, ip6h);
> + IOV_PUT_HEADER(¤t, uh);
> }
>
> return l4len;
> @@ -165,9 +167,10 @@ static void udp_vu_csum(const struct flowside *toside,
> struct iov_tail payload = *data;
> struct udphdr *uh, uh_storage;
> bool ipv4 = src4 && dst4;
> + int hdrlen = sizeof(struct ethhdr) +
> + (ipv4 ? sizeof(struct iphdr) : sizeof(struct ipv6hdr));
>
> - iov_drop_header(&payload,
> - udp_vu_hdrlen(!ipv4) - sizeof(struct udphdr));
> + iov_drop_header(&payload, hdrlen);
> uh = IOV_REMOVE_HEADER(&payload, uh_storage);
>
> if (ipv4)
> @@ -207,9 +210,9 @@ void udp_vu_sock_to_tap(const struct ctx *c, int s, int n, flow_sidx_t tosidx)
> }
>
> for (i = 0; i < n; i++) {
> + int elem_cnt, elem_used;
> size_t iov_cnt;
> ssize_t dlen;
> - int elem_cnt;
>
> vu_init_elem(elem, iov_vu, ARRAY_SIZE(elem));
>
> @@ -224,19 +227,20 @@ void udp_vu_sock_to_tap(const struct ctx *c, int s, int n, flow_sidx_t tosidx)
> vu_queue_rewind(vq, elem_cnt);
> break;
> }
> + elem_used = iov_cnt;
I don't see under circumstances elem_used would become different from
iov_cnt (and therefore I'm not sure why elem_used is needed).
>
> /* release unused buffers */
> - vu_queue_rewind(vq, elem_cnt - iov_cnt);
> -
> + vu_queue_rewind(vq, elem_cnt - elem_used);
> if (iov_cnt > 0) {
> struct iov_tail data = IOV_TAIL(iov_vu, iov_cnt, 0);
> - vu_set_vnethdr(iov_vu[0].iov_base, iov_cnt);
> - udp_vu_prepare(c, &data, toside, dlen);
> + vu_set_vnethdr(iov_vu[0].iov_base, elem_used);
> + iov_drop_header(&data, VNET_HLEN);
> + udp_vu_prepare(c, &data, toside);
> if (*c->pcap) {
> udp_vu_csum(toside, &data);
> - pcap_iov(data.iov, data.cnt, VNET_HLEN);
> + pcap_iov(data.iov, data.cnt, data.off);
A pcap_iov_tail() might be a useful at some point.
> }
> - vu_flush(vdev, vq, elem, data.cnt);
> + vu_flush(vdev, vq, elem, elem_used);
> }
> }
> }
> --
> 2.53.0
>
--
David Gibson (he or they) | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you, not the other way
| around.
http://www.ozlabs.org/~dgibson
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
next prev parent reply other threads:[~2026-03-12 4:30 UTC|newest]
Thread overview: 28+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-09 9:47 [PATCH v2 00/13] vhost-user,udp: Handle multiple iovec entries per virtqueue element Laurent Vivier
2026-03-09 9:47 ` [PATCH v2 01/13] iov: Add iov_truncate() helper and use it in vu handlers Laurent Vivier
2026-03-09 9:47 ` [PATCH v2 02/13] vhost-user: Centralise 802.3 frame padding in vu_collect() and vu_flush() Laurent Vivier
2026-03-12 2:05 ` David Gibson
2026-03-09 9:47 ` [PATCH v2 03/13] vhost-user: Use ARRAY_SIZE(elem) instead of VIRTQUEUE_MAX_SIZE Laurent Vivier
2026-03-09 9:47 ` [PATCH v2 04/13] udp_vu: Use iov_tail to manage virtqueue buffers Laurent Vivier
2026-03-12 2:38 ` David Gibson
2026-03-09 9:47 ` [PATCH v2 05/13] udp_vu: Move virtqueue management from udp_vu_sock_recv() to its caller Laurent Vivier
2026-03-12 3:44 ` David Gibson
2026-03-09 9:47 ` [PATCH v2 06/13] iov: Add IOV_PUT_HEADER() to write header data back to iov_tail Laurent Vivier
2026-03-12 4:12 ` David Gibson
2026-03-12 8:20 ` Laurent Vivier
2026-03-09 9:47 ` [PATCH v2 07/13] udp: Pass iov_tail to udp_update_hdr4()/udp_update_hdr6() Laurent Vivier
2026-03-12 4:16 ` David Gibson
2026-03-09 9:47 ` [PATCH v2 08/13] udp_vu: Use iov_tail in udp_vu_prepare() Laurent Vivier
2026-03-12 4:30 ` David Gibson [this message]
2026-03-12 8:19 ` Laurent Vivier
2026-03-12 9:51 ` David Gibson
2026-03-09 9:47 ` [PATCH v2 09/13] vu_common: Pass iov_tail to vu_set_vnethdr() Laurent Vivier
2026-03-12 4:34 ` David Gibson
2026-03-09 9:47 ` [PATCH v2 10/13] vu_common: Accept explicit iovec counts in vu_set_element() Laurent Vivier
2026-03-09 9:47 ` [PATCH v2 11/13] vu_common: Accept explicit iovec count per element in vu_init_elem() Laurent Vivier
2026-03-09 9:47 ` [PATCH v2 12/13] vu_common: Prepare to use multibuffer with guest RX Laurent Vivier
2026-03-09 9:47 ` [PATCH v2 13/13] vhost-user,udp: Use 2 iovec entries per element Laurent Vivier
2026-03-12 4:39 ` David Gibson
2026-03-12 8:08 ` Laurent Vivier
2026-03-12 9:47 ` David Gibson
2026-03-12 10:42 ` Laurent Vivier
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=abJBVhDtr1vGOndC@zatzit \
--to=david@gibson.dropbear.id.au \
--cc=lvivier@redhat.com \
--cc=passt-dev@passt.top \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://passt.top/passt
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for IMAP folder(s).