From: Laurent Vivier <lvivier@redhat.com>
To: aerosouund <aerosound161@gmail.com>, passt-dev@passt.top
Subject: Re: [PATCH v2 6/7] tap: Implement the pasta vhost-net to-guest path
Date: Tue, 10 Nov 2026 15:36:35 +0100 [thread overview]
Message-ID: <ba2de1a8-78f9-4a91-985b-3fb9336dc500@redhat.com> (raw)
In-Reply-To: <20261016204940.33190-7-aerosound161@gmail.com>
On 10/16/26 22:49, aerosouund wrote:
> From: Ammar Yasser <aerosound161@gmail.com>
>
> Add a vhost argument to tap_send_frames_pasta(). Callers pass it to say
> whether this send should go through vhost-net, and when it does sending
> goes through tap_send_frames_vhost(), which takes a descriptor from the
> queue shared with the kernel for each iov and points it at that iov's
> base address, chaining the bufs_per_frame descriptors of a frame
> together so the guest receives it in one go.
>
> tx_reap() reclaims the descriptors the kernel has finished with, walking
> each used chain to its end so that every descriptor in it is counted as
> free again.
>
> Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
> Signed-off-by: Ammar Yasser <aerosound161@gmail.com>
> ---
> tap.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
> 1 file changed, 147 insertions(+), 3 deletions(-)
>
> diff --git a/tap.c b/tap.c
> index fd500f1..bc2700f 100644
> --- a/tap.c
> +++ b/tap.c
> @@ -359,12 +359,151 @@ void tap_icmp6_send(const struct ctx *c,
> tap_send_single(c, buf, l4len + ((char *)icmp6h - buf));
> }
>
> +/**
> + * tx_reap() - Reclaim the descriptors the kernel has already processed
> + */
> +static void tx_reap(void)
> +{
> + struct vring_used_pasta *used = &vring_used_all[VHOST_USED_Q_IDX];
> + uint16_t used_idx = le16toh(used->idx);
> +
> + smp_rmb();
> +
> + /* increment last_used_idx until it reaches the kernel's used index */
> + while (vhost_vq_state[VHOST_USED_Q_IDX].last_used_idx != used_idx) {
> + uint16_t last_used;
> + uint32_t desc_id;
> +
> + last_used = vhost_vq_state[VHOST_USED_Q_IDX].last_used_idx % VHOST_NDESCS;
> + desc_id = le32toh(used->ring[last_used].id);
> +
> + for (;;) {
We should check we don't have a circular chain rather than allowing an infinite loop
> + /* keep going until we find a descriptor without the
> + * next flag
> + */
> + vhost_vq_state[VHOST_USED_Q_IDX].num_free++;
Check desc_id < VHOST_NDESCS
> + if (!(le16toh(vring_desc[VHOST_USED_Q_IDX][desc_id].flags) &
> + VRING_DESC_F_NEXT))
> + break;
> +
> + /* this descriptor wasn't the last, set desc_id to the
> + * next one and keep going
> + */
> + desc_id = le32toh(vring_desc[VHOST_USED_Q_IDX][desc_id].next);
.next is __virtio16(), le16toh().
> + }
> +
> + vhost_vq_state[VHOST_USED_Q_IDX].last_used_idx++;
> + }
> +}
> +
> +/**
> + * tap_send_frames_vhost() - Send multiple frames to the pasta tap
> + * @c: Execution context
> + * @iov: Array of buffers
> + * @bufs_per_frame: Number of buffers (iovec entries) per frame
> + * @nframes: Number of frames to send
> + *
> + * @iov must have total length @bufs_per_frame * @nframes, with each set of
> + * @bufs_per_frame contiguous buffers representing a single frame.
> + *
> + * Return: number of frames successfully sent
> + */
> +static size_t tap_send_frames_vhost(const struct ctx *c,
> + const struct iovec *iov,
> + size_t bufs_per_frame, size_t nframes)
> +{
> + struct vring_avail_pasta *avail =
> + &vring_avail_all[VHOST_USED_Q_IDX];
> + size_t processed_frames = 0;
> + size_t i;
> +
> + /* reclaim descriptors if we don't have enough available buffers to
> + * perform this send
> + */
> + if (vhost_vq_state[VHOST_USED_Q_IDX].num_free < bufs_per_frame * nframes)
> + tx_reap();
> +
> + for (i = 0; i < nframes; i++) {
> + uint16_t head;
> + size_t j;
> +
> + /* it's likely that tx_reap returned to us less than
> + * bufs_per_frame descs
> + */
> + if (vhost_vq_state[VHOST_USED_Q_IDX].num_free < bufs_per_frame)
Perhaps you can add a log message here.
> + break;
> +
> + /* set the index of the avail ring in the tx queue to be our
> + * last_used_idx
> + */
> + head = vhost_vq_state[VHOST_USED_Q_IDX].next_free % VHOST_NDESCS;
> + avail->ring[(avail->idx + i) % VHOST_NDESCS] = htole16(head);
> +
> + /* we will be consuming bufs_per_frame descriptors for every
> + * frame, decrement the local num_free
> + */
> + vhost_vq_state[VHOST_USED_Q_IDX].num_free -= bufs_per_frame;
> +
> + for (j = 0; j < bufs_per_frame; ++j) {
> + uint16_t next =
> + vhost_vq_state[VHOST_USED_Q_IDX].next_free %
> + VHOST_NDESCS;
> + /* get the last_used_idx descriptor */
> + struct vring_desc_pasta *desc =
> + &vring_desc[VHOST_USED_Q_IDX][next];
> + const struct iovec *iov_i;
> +
> + /* the iov variable contains the iovecs for all frames
> + * we will send. access a single fragment of a frame
> + * (each fragment is one of tcp_iov_parts) denoted by
> + * iov at index i (index of the frame being processed)
> + * * bufs_per_frame plus j (the index of the fragment
> + * being processed)
> + */
> + iov_i = &iov[i * bufs_per_frame + j];
> +
> + /* set that descriptor's address to the base of the iov
> + * and set the VRING_DESC_F_NEXT flag on the descriptor
> + * if its not the last frame fragment, so that the
> + * guest would recieve the entire frame in one go.
> + */
I think you should skip the iov when iov_len is 0. I can happen with UDP_IOV_ETH_PAD.
> + desc->addr = (uint64_t)iov_i->iov_base;
We need htole64()
> + desc->len = iov_i->iov_len;
htole32()
> + desc->flags = (j == bufs_per_frame - 1) ?
> + 0 : htole16(VRING_DESC_F_NEXT);
> +
> + vhost_vq_state[VHOST_USED_Q_IDX].next_free++;
> + }
> +
> + processed_frames++;
> + }
> +
> + /* we didn't process any frames, no need to notify the kernel */
> + if (!processed_frames)
> + return 0;
> +
> + smp_wmb();
> +
> + /* we will have used nframes descriptor chains */
> + avail->idx = htole16(le16toh(avail->idx) + processed_frames);
The value to increment is not processed_frames but the number of desc that have been consumed.
> +
> + vhost_kick(&vring_used_all[VHOST_USED_Q_IDX],
> + c->vhost.vq[VHOST_USED_Q_IDX].kick_fd);
> +
> + /* wait until the kernel finishes processing this send */
> + while (avail->idx != vring_used_all[1].used.idx)
> + ;
> +
> + return processed_frames;
> +}
> +
> /**
> * tap_send_frames_pasta() - Send multiple frames to the pasta tap
> * @c: Execution context
> * @iov: Array of buffers
> * @bufs_per_frame: Number of buffers (iovec entries) per frame
> * @nframes: Number of frames to send
> + * @vhost: Send through vhost-net rather than writing to the tap
> *
> * @iov must have total length @bufs_per_frame * @nframes, with each set of
> * @bufs_per_frame contiguous buffers representing a single frame.
> @@ -375,11 +514,15 @@ void tap_icmp6_send(const struct ctx *c,
> */
> static size_t tap_send_frames_pasta(const struct ctx *c,
> const struct iovec *iov,
> - size_t bufs_per_frame, size_t nframes)
> + size_t bufs_per_frame, size_t nframes,
> + bool vhost)
> {
> size_t nbufs = bufs_per_frame * nframes;
> size_t i;
>
> + if (vhost)
> + return tap_send_frames_vhost(c, iov, bufs_per_frame, nframes);
> +
> for (i = 0; i < nbufs; i += bufs_per_frame) {
> ssize_t rc = writev(c->fd_tap, iov + i, bufs_per_frame);
> size_t framelen = iov_size(iov + i, bufs_per_frame);
> @@ -515,7 +658,7 @@ void tap_send_single(const struct ctx *c, const void *data, size_t l2len)
> iov[iovcnt].iov_len = l2len;
> iovcnt++;
>
> - m = tap_send_frames_pasta(c, iov, iovcnt, 1);
> + m = tap_send_frames_pasta(c, iov, iovcnt, 1, false);
> break;
> case MODE_VU:
> vu_send_single(c, data, l2len);
> @@ -553,7 +696,8 @@ size_t tap_send_frames(const struct ctx *c, const struct iovec *iov,
>
> switch (c->mode) {
> case MODE_PASTA:
> - m = tap_send_frames_pasta(c, iov, bufs_per_frame, nframes);
> + m = tap_send_frames_pasta(c, iov, bufs_per_frame, nframes,
> + c->vhost.fd != -1);
> break;
> case MODE_PASST:
> m = tap_send_frames_passt(c, iov, bufs_per_frame, nframes);
> --
> 2.39.5 (Apple Git-154)
next prev parent reply other threads:[~2026-11-10 14:36 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-16 20:49 [PATCH v2 0/7] Add vhost-net kernel support to pasta aerosouund
2026-10-16 20:49 ` [PATCH v2 1/7] tap: Move the tap_hdr file to a separate file aerosouund
2026-11-06 10:47 ` Laurent Vivier
2026-10-16 20:49 ` [PATCH v2 2/7] conf: Add context fields, epoll types and --vhost-kernel flag to pasta aerosouund
2026-11-06 15:34 ` Laurent Vivier
2026-10-16 20:49 ` [PATCH v2 3/7] virtio: Add the pasta vhost-net interface and implementation aerosouund
2026-11-06 17:00 ` Laurent Vivier
2026-11-09 15:07 ` Laurent Vivier
2026-10-16 20:49 ` [PATCH v2 4/7] tap: Implement the pasta vhost-net from-guest path aerosouund
2026-11-09 15:37 ` Laurent Vivier
2026-10-16 20:49 ` [PATCH v2 5/7] tap, tcp, udp: Prepare the to-guest path for vhost-net aerosouund
2026-11-09 16:41 ` Laurent Vivier
2026-10-16 20:49 ` [PATCH v2 6/7] tap: Implement the pasta vhost-net to-guest path aerosouund
2026-11-10 14:36 ` Laurent Vivier [this message]
2026-10-16 20:49 ` [PATCH v2 7/7] tap/tcp: Replace tcp_payload_used with a ring buffer style index aerosouund
2026-11-10 17:56 ` 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=ba2de1a8-78f9-4a91-985b-3fb9336dc500@redhat.com \
--to=lvivier@redhat.com \
--cc=aerosound161@gmail.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).