From: aerosouund <aerosound161@gmail.com>
To: passt-dev@passt.top
Cc: eperezma@redhat.com, Ammar Yasser <aerosound161@gmail.com>
Subject: [PATCH 4/7] tap: Implement the pasta vhost-net from-guest path
Date: Sat, 5 Sep 2026 19:25:27 +0300 [thread overview]
Message-ID: <20260905162528.43564-3-aerosound161@gmail.com> (raw)
In-Reply-To: <20260905162528.43564-1-aerosound161@gmail.com>
From: Ammar Yasser <aerosound161@gmail.com>
Add tap_vhost_input(), called when the kernel signals on a call eventfd
that it has written guest frames into the from-guest queue. It consumes
descriptors with consume_one_rx_descriptor(), skips the virtio_net header
on each, builds an iov_tail from what's left and queues it for
processing, then tells the kernel the descriptors are free to reuse.
consume_one_rx_descriptor() pops one entry from the used ring, advances
our read cursor and the count of descriptors awaiting handoff, and
returns a pointer to that descriptor's data.
Bootstrap vhost in tap_sock_tun_init(). If setup fails we leave
c->vhost.fd at -1 and carry on unaccelerated.
With vhost-net running the kernel reads the tap fd itself, so don't add
that fd to our epoll set at all: tap_handler_pasta() would otherwise be
woken for data it must not read.
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
Signed-off-by: Ammar Yasser <aerosound161@gmail.com>
---
passt.c | 6 +++
tap.c | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
tap.h | 2 +
3 files changed, 119 insertions(+), 1 deletion(-)
diff --git a/passt.c b/passt.c
index 6002231..372b743 100644
--- a/passt.c
+++ b/passt.c
@@ -305,6 +305,12 @@ static void passt_worker(void *opaque, int nfds, struct epoll_event *events)
case EPOLL_TYPE_CONF:
conf_handler(c, eventmask);
break;
+ case EPOLL_TYPE_VHOST_CALL:
+ tap_vhost_input(c, ref, &now);
+ break;
+ case EPOLL_TYPE_VHOST_ERROR:
+ die("Error on vhost-kernel socket");
+ break;
default:
/* Can't happen */
assert(0);
diff --git a/tap.c b/tap.c
index dfa66c7..f84cb0a 100644
--- a/tap.c
+++ b/tap.c
@@ -38,6 +38,7 @@
#include <netinet/udp.h>
#include <netinet/ip_icmp.h>
#include <netinet/if_ether.h>
+#include <sys/eventfd.h>
#include <linux/if_tun.h>
#include <linux/icmpv6.h>
@@ -61,6 +62,7 @@
#include "vhost_user.h"
#include "vu_common.h"
#include "epoll_ctl.h"
+#include "vhost.h"
/* Maximum allowed frame lengths (including L2 header) */
@@ -1446,7 +1448,11 @@ static void tap_start_connection(const struct ctx *c)
break;
}
- epoll_add(c->epollfd, EPOLLIN | EPOLLRDHUP, ref);
+ /* With vhost-net running, the kernel reads the tap fd for us and
+ * notifies us on the call eventfd, so we mustn't watch it ourselves
+ */
+ if (c->vhost.fd == -1)
+ epoll_add(c->epollfd, EPOLLIN | EPOLLRDHUP, ref);
if (!tap_is_ready(c))
return;
@@ -1514,6 +1520,89 @@ void tap_listen_handler(struct ctx *c, uint32_t events)
tap_start_connection(c);
}
+/**
+ * consume_one_rx_descriptor() - Consume one used from-guest descriptor
+ * @len: Set to the length of data written by the kernel
+ *
+ * Pops a single entry from the used ring, advancing our read cursor and the
+ * count of descriptors awaiting refill announcement.
+ *
+ * NOTE: This function assumes the kernel is going to post single descriptors
+ * always, No chains. If that changes, we would need to increment num_free
+ * as we advertise back to the kernel the free descriptors by the length of the chain.
+ *
+ * Return: pointer to the packet buffer, or NULL if no data is available
+ */
+static void *consume_one_rx_descriptor(size_t *len)
+{
+ struct vring_used *used = &vring_used_all[0].used;
+ uint16_t used_idx, last_used;
+ uint32_t i;
+
+ used_idx = le16toh(used->idx);
+
+ smp_rmb();
+
+ /* If the kernel's used index matches ours, we've consumed everything
+ * it has posted
+ */
+ if (used_idx == vhost_vq_state[0].last_used_idx) {
+ *len = 0;
+ return NULL;
+ }
+
+ last_used = vhost_vq_state[0].last_used_idx % VHOST_NDESCS;
+ i = le32toh(used->ring[last_used].id);
+ *len = le32toh(used->ring[last_used].len);
+
+ if (i != last_used)
+ die("vhost: id %u at used position %u", i, last_used);
+
+ /* The kernel has queued for us something we cannot receive */
+ if (*len > VHOST_DESC_BYTES)
+ die("vhost: id %u len %zu > %zu", i, *len,
+ (size_t)VHOST_DESC_BYTES);
+
+ vhost_vq_state[0].last_used_idx++;
+ vhost_vq_state[0].num_free++;
+
+ return pkt_buf + i * VHOST_DESC_BYTES;
+}
+
+/**
+ * tap_vhost_input() - Handle frames the kernel wrote to the from-guest queue
+ * @c: Execution context
+ * @ref: epoll reference
+ * @now: Current timestamp
+ */
+void tap_vhost_input(struct ctx *c, union epoll_ref ref,
+ const struct timespec *now)
+{
+ struct virtio_net_hdr_mrg_rxbuf *hdr;
+ struct iov_tail data;
+ size_t len;
+
+ eventfd_read(ref.fd, (eventfd_t[]){ 0 });
+
+ tap_flush_pools();
+
+ while ((hdr = consume_one_rx_descriptor(&len))) {
+ /* a valid receive is one where there is a virtio net header followed by atleast sizeof(struct ethhdr) bytes */
+ if (len < (sizeof(*hdr) + sizeof(struct ethhdr))) {
+ warn("vhost: invalid len %lu", len);
+ continue;
+ }
+
+ /* Skip over the vnet header, add the packet without it */
+ data = IOV_TAIL_FROM_BUF((void *)(hdr + 1),
+ len - sizeof(*hdr), 0);
+ tap_add_packet(c, &data, now);
+ }
+
+ tap_handler(c, now);
+ vhost_rx_descriptor_handoff(c);
+}
+
/**
* tap_ns_tun() - Get tuntap fd in namespace
* @c: Execution context
@@ -1555,12 +1644,33 @@ static int tap_ns_tun(void *arg)
*/
static void tap_sock_tun_init(struct ctx *c)
{
+ int i;
+
if (!c->splice_only) {
NS_CALL(tap_ns_tun, c);
if (c->fd_tap == -1)
die("Failed to set up tap device in namespace");
}
+ /* Nothing else here runs if this fails: c->vhost.fd stays -1 and we
+ * carry on reading the tap fd ourselves, unless acceleration was
+ * explicitly asked for
+ */
+ if (c->vhost.mode != VHOST_MODE_OFF && vhost_setup_net(c) < 0 &&
+ c->vhost.mode == VHOST_MODE_ON)
+ die("vhost-net is not available");
+
+ if (c->vhost.fd != -1) {
+ for (i = 0; i < ARRAY_SIZE(c->vhost.vq); i++)
+ vhost_setup_eventfds(c, i);
+
+ if (vhost_setup_memory_table(c) < 0)
+ die_perror("VHOST_SET_MEM_TABLE ioctl failed");
+
+ for (i = 0; i < ARRAY_SIZE(c->vhost.vq); i++)
+ vhost_set_vring(c, i, c->fd_tap);
+ }
+
pasta_ns_conf(c);
if (!c->splice_only)
diff --git a/tap.h b/tap.h
index ce1efa8..b0d70f8 100644
--- a/tap.h
+++ b/tap.h
@@ -48,6 +48,8 @@ static inline void tap_hdr_update(struct tap_hdr *thdr, size_t l2len)
thdr->vnet_len = htonl(l2len);
}
+void tap_vhost_input(struct ctx *c, union epoll_ref ref,
+ const struct timespec *now);
unsigned long tap_l2_max_len(const struct ctx *c);
void *tap_push_l2h(const struct ctx *c, void *buf,
const void *src_mac, uint16_t proto);
--
2.39.5 (Apple Git-154)
next prev parent reply other threads:[~2026-09-05 16:26 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-04 21:28 [PATCH 0/7] Add vhost-net kernel support to pasta aerosouund
2026-09-04 21:28 ` [PATCH 1/7] tap: Move the tap_hdr file to a separate file aerosouund
2026-09-05 1:52 ` David Gibson
2026-09-04 21:28 ` [PATCH 2/7] conf: Add context fields, epoll types and --vhost-kernel flag to pasta aerosouund
2026-09-04 21:28 ` [PATCH 3/7] virtio: Add the pasta vhost-net interface and implementation aerosouund
2026-09-04 21:28 ` [PATCH 4/7] tap: Implement the pasta vhost-net from-guest path aerosouund
2026-09-04 21:28 ` [PATCH 5/7] tap, tcp, udp: Prepare the to-guest path for vhost-net aerosouund
2026-09-04 21:28 ` [PATCH 6/7] tap: Implement the pasta vhost-net to-guest path aerosouund
2026-09-04 21:28 ` [PATCH 7/7] tap/tcp: Replace tcp_payload_used with a ring buffer style index aerosouund
2026-09-05 16:25 ` [PATCH 2/7] conf: Add context fields, epoll types and --vhost-kernel flag to pasta aerosouund
2026-09-05 16:25 ` [PATCH 3/7] virtio: Add the pasta vhost-net interface and implementation aerosouund
2026-09-05 16:25 ` aerosouund [this message]
2026-09-05 16:25 ` [PATCH 7/7] tap/tcp: Replace tcp_payload_used with a ring buffer style index aerosouund
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=20260905162528.43564-3-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).