From: Ammar Yasser <aerosound161@gmail.com>
To: passt-dev@passt.top
Cc: eperezma@redhat.com, Ammar Yasser <aerosound161@gmail.com>
Subject: [RFC v3 8/8] tap: Implement pasta vhost TX
Date: Sun, 2 Aug 2026 13:21:55 +0000 [thread overview]
Message-ID: <20260802132155.870796-9-aerosound161@gmail.com> (raw)
In-Reply-To: <20260802132155.870796-1-aerosound161@gmail.com>
Callers will specify whether vhost should be used or no by passing the
vhost argument to tap_send_frames_pasta. If specified, sending will go
through a function called tap_send_frames_vhost, which pops a descriptor
from the queue shared with the kernel and sets the address of that
descriptor to be the base address of a single iov from the group of
buffers_per_frame * nframes iovs we pass to the function
Signed-off-by: Ammar Yasser <aerosound161@gmail.com>
---
tap.c | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 107 insertions(+), 1 deletion(-)
diff --git a/tap.c b/tap.c
index e177eef..66bb71d 100644
--- a/tap.c
+++ b/tap.c
@@ -361,7 +361,32 @@ void tap_icmp6_send(const struct ctx *c,
}
/**
- * tap_send_frames_pasta() - Send multiple frames to the pasta tap
+ * tx_reap() - Reclaim the descriptors the kernel has already processed
+ */
+static void tx_reap(void) {
+ struct vring_used *used = &vring_used_all[1].used;
+ uint16_t used_idx = le16toh(used->idx);
+
+ smp_rmb();
+
+ /* increment last_used_idx until it reaches the kernel's used index */
+ while (vqs[1].last_used_idx != used_idx) {
+ uint16_t desc_id = le32toh(used->ring[vqs[1].last_used_idx % VHOST_NDESCS].id);
+
+ for (;;) {
+ /* keep going until we find a descriptor without the next flag */
+ vqs[1].num_free++;
+ if (!(le16toh(vring_desc[1][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 = le16toh(vring_desc[1][desc_id].next);
+ }
+ vqs[1].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
@@ -371,6 +396,84 @@ void tap_icmp6_send(const struct ctx *c,
* @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)
+{
+ size_t i;
+ size_t processed_frames = 0;
+
+ /* update our local counters first */
+ tx_reap();
+
+ #define AVAIL_Q(i)(vring_avail_all[i].avail)
+
+ for (i = 0; i < nframes; i++) {
+ size_t j;
+
+ if (vqs[1].num_free < bufs_per_frame)
+ break;
+
+ /* set the index of the avail ring in the tx queue to be our last_used_idx */
+ uint16_t head = vqs[1].next_free % VHOST_NDESCS;
+ AVAIL_Q(1).ring[(AVAIL_Q(1).idx + i) % VHOST_NDESCS] = htole16(head);
+
+ /* we will be consuming bufs_per_frame descriptors for every frame, decrement the local num_free */
+ vqs[1].num_free -= bufs_per_frame;
+
+ for (j = 0; j < bufs_per_frame; ++j) {
+ /* get the last_used_idx descriptor */
+ struct vring_desc *desc = &vring_desc[1][vqs[1].next_free % VHOST_NDESCS];
+ /*
+ * 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)
+ */
+ const struct iovec *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.
+ */
+ desc->addr = (uint64_t)iov_i->iov_base;
+ desc->len = iov_i->iov_len;
+ desc->flags = (j == bufs_per_frame - 1) ? 0 : htole16(VRING_DESC_F_NEXT);
+ vqs[1].next_free++;
+ }
+
+ processed_frames++;
+ }
+
+ /* we didn't process any frames, no need to notify the kernel */
+ if ((processed_frames == 0))
+ return 0;
+
+ smp_wmb();
+ /* we will have used nframes descriptor chains */
+ AVAIL_Q(1).idx = htole16(le16toh(AVAIL_Q(1).idx)+processed_frames);
+ #undef AVAIL_Q
+
+ vhost_kick(&vring_used_all[1].used, c->vq[1].kick_fd);
+
+ 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: Use vhost-kernel or not
+ *
+ * @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 (or queued)
*
* #syscalls:pasta write
*/
@@ -381,6 +484,9 @@ static size_t tap_send_frames_pasta(const struct ctx *c,
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);
--
2.34.1
prev parent reply other threads:[~2026-08-02 13:23 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-02 13:21 [RFC v3 0/8] Add vhost-net kernel support to pasta Ammar Yasser
2026-08-02 13:21 ` [RFC v3 1/8] tap: Move the tap_hdr file to a separate file Ammar Yasser
2026-08-02 13:21 ` [RFC v3 2/8] udp,tcp: Make protocol specific buffers public Ammar Yasser
2026-08-02 13:21 ` [RFC v3 3/8] conf: Add context fields, epoll types and the --vhost flag to pasta Ammar Yasser
2026-08-02 13:21 ` [RFC v3 4/8] virtio: Define the pasta vhost interface Ammar Yasser
2026-08-02 13:21 ` [RFC v3 5/8] virtio: Implement the pasta vhost functions Ammar Yasser
2026-08-02 13:21 ` [RFC v3 6/8] virtio: Implement pasta vhost acceleration guest->pasta path Ammar Yasser
2026-08-02 13:21 ` [RFC v3 7/8] pasta: Implement pasta vhost TX (pasta->guest) prerequisites Ammar Yasser
2026-08-02 13:21 ` Ammar Yasser [this message]
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=20260802132155.870796-9-aerosound161@gmail.com \
--to=aerosound161@gmail.com \
--cc=eperezma@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).