public inbox for passt-dev@passt.top
 help / color / mirror / code / Atom feed
* [PATCH 0/7] Add vhost-net kernel support to pasta
@ 2026-09-04 21:28 aerosouund
  2026-09-04 21:28 ` [PATCH 1/7] tap: Move the tap_hdr file to a separate file aerosouund
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: aerosouund @ 2026-09-04 21:28 UTC (permalink / raw)
  To: passt-dev; +Cc: eperezma, aerosouund

This work is based on the original v2 RFC by Eugenio Perez:

Link:
https://archives.passt.top/passt-dev/20250709174748.3514693-1-eperezma@redhat.com/

It is the first finished patch series for vhost-kernel support after a
third RFC was submitted:

Link:
https://archives.passt.top/passt-dev/20260802132155.870796-1-aerosound161@gmail.com/

vhost-net is a kernel device that allows reading and writing packets to a
tap device using virtqueues instead of read(2), write(2), and their
vectored counterparts. Frames are handed over through a shared descriptor
ring, so several of them can be passed to the kernel at once rather than
issuing a syscall per frame.

The v3 RFC didn't yield any performance improvements because, to retain
correctness, sends between pasta and the vhost-kernel thread had to be
synchronous. This patch series fixes this problem through the changes
introduced in the last commit, titled:

"tap/tcp: Replace tcp_payload_used with a ring buffer style index"

The changes reliably yield a roughly 2x speedup on the TX path (container
to host).

Numbers were obtained through iperf3 with the following methodology and
results:

Pasta command:
./pasta -f --runas 0:0 --map-guest-addr 192.168.0.5 
--vhost-kernel on --config-net 2729785

Host:
iperf3 -s1J

Container:
iperf3 -c 192.168.0.5 -t 10 -P4 -l 1M -w 32M -i1

With --vhost-kernel on:

[SUM]   0.00-10.00  sec  1.36 GBytes  1.17 Gbits/sec  3643 sender
[SUM]   0.00-10.00  sec  1.16 GBytes   999 Mbits/sec        receiver

With --vhost-kernel off:

[SUM]   0.00-10.00  sec   700 MBytes   587 Mbits/sec  3556 sender
[SUM]   0.00-10.10  sec   495 MBytes   411 Mbits/sec        receiver

Ammar Yasser (7):
  tap: Move the tap_hdr file to a separate file
  conf: Add context fields, epoll types and --vhost-kernel flag to pasta
  virtio: Add the pasta vhost-net interface and implementation
  tap: Implement the pasta vhost-net from-guest path
  tap, tcp, udp: Prepare the to-guest path for vhost-net
  tap: Implement the pasta vhost-net to-guest path
  tap/tcp: Replace tcp_payload_used with a ring buffer style index

 Makefile     |   2 +-
 conf.c       |  19 +++
 epoll_type.h |   4 +
 passt.c      |   9 ++
 passt.h      |  79 ++++++++++-
 tap.c        | 364 ++++++++++++++++++++++++++++++++++++++++++++-------
 tap.h        |  47 ++-----
 tap_hdr.h    |  23 ++++
 tcp_buf.c    | 143 +++++++++++++++-----
 tcp_buf.h    |   4 +
 udp.c        |  41 ++++--
 udp.h        |   3 +
 util.c       |  12 ++
 util.h       |   2 +
 vhost.c      | 295 +++++++++++++++++++++++++++++++++++++++++
 vhost.h      |  79 +++++++++++
 16 files changed, 1002 insertions(+), 124 deletions(-)
 create mode 100644 tap_hdr.h
 create mode 100644 vhost.c
 create mode 100644 vhost.h

-- 
2.39.5 (Apple Git-154)


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 1/7] tap: Move the tap_hdr file to a separate file
  2026-09-04 21:28 [PATCH 0/7] Add vhost-net kernel support to pasta aerosouund
@ 2026-09-04 21:28 ` aerosouund
  2026-09-05  1:52   ` David Gibson
  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
  2 siblings, 1 reply; 5+ messages in thread
From: aerosouund @ 2026-09-04 21:28 UTC (permalink / raw)
  To: passt-dev; +Cc: eperezma, Ammar Yasser

From: Ammar Yasser <aerosound161@gmail.com>

It was defined in tap.h, put it on its own so that future callers won't
need to depend on all definitions in tap.h.
Also turn it into a union of a vnet_len and virtio_net_mrg_rxbuf
because for vhost acceleration the frames will have this virtio net
header prepended to them.

Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
Signed-off-by: Ammar Yasser <aerosound161@gmail.com>
---
 tap.h     |  9 +--------
 tap_hdr.h | 23 +++++++++++++++++++++++
 2 files changed, 24 insertions(+), 8 deletions(-)
 create mode 100644 tap_hdr.h

diff --git a/tap.h b/tap.h
index b335933..1625975 100644
--- a/tap.h
+++ b/tap.h
@@ -10,6 +10,7 @@
 #include <stdint.h>
 
 #include "passt.h"
+#include "tap_hdr.h"
 
 /** L2_MAX_LEN_PASTA - Maximum frame length for pasta mode (with L2 header)
  *
@@ -38,14 +39,6 @@
 
 struct udphdr;
 
-/**
- * struct tap_hdr - tap backend specific headers
- * @vnet_len:	Frame length (for qemu socket transport)
- */
-struct tap_hdr {
-	uint32_t vnet_len;
-} __attribute__((packed));
-
 /**
  * tap_hdr_iov() - struct iovec for a tap header
  * @c:		Execution context
diff --git a/tap_hdr.h b/tap_hdr.h
new file mode 100644
index 0000000..aa270b7
--- /dev/null
+++ b/tap_hdr.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright (c) 2021 Red Hat GmbH
+ * Author: Stefano Brivio <sbrivio@redhat.com>
+ */
+
+#ifndef TAP_HDR_H
+#define TAP_HDR_H
+
+#include <stdint.h>
+#include <linux/virtio_net.h>
+
+/**
+ * struct tap_hdr - tap backend specific headers
+ * @vnet_len:	Frame length (for qemu socket transport)
+ */
+struct tap_hdr {
+	union {
+		uint32_t vnet_len;
+		struct virtio_net_hdr_mrg_rxbuf hdr;
+	};
+};
+
+#endif /* TAP_HDR_H */
-- 
2.39.5 (Apple Git-154)


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 5/7] tap, tcp, udp: Prepare the to-guest path for vhost-net
  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-04 21:28 ` aerosouund
  2026-09-04 21:28 ` [PATCH 6/7] tap: Implement the pasta vhost-net to-guest path aerosouund
  2 siblings, 0 replies; 5+ messages in thread
From: aerosouund @ 2026-09-04 21:28 UTC (permalink / raw)
  To: passt-dev; +Cc: eperezma, Ammar Yasser

From: Ammar Yasser <aerosound161@gmail.com>

Frames sent through vhost-net carry a virtio-net header where pasta's
tap header would otherwise go, this necessitates the following two
changes:

- udp_tap_prepare() has been modified to take a context parameter so it
  can check if vhost is set up. Only if it isn't should tap_hdr_update
  be called because this means that the active union variant is vnet_len
  no the virtio net header
- tap_l2_offset() a new helper, has been introduced which tells
  pcap_multiple() how many bytes precede the L2 frame, which now varies
  with vhost as well as with the mode

tap_send_single() calls tap_send_frames_passt() and
tap_send_frames_pasta() directly rather than going through
tap_send_frames(). Its callers - ARP, TCP resets, and DHCP, DHCPv6, NDP
and ICMP via tap_udp*_send()/tap_icmp*_send() - build their frame in a
buffer on the stack, not in the buffers registered with vhost-net. The
kernel can only read from registered memory, so these protocols can
never take the vhost path and shouldn't be tested for it on every frame.

Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
Signed-off-by: Ammar Yasser <aerosound161@gmail.com>
---
 tap.c     | 112 ++++++++++++++++++++++++++++++++++--------------------
 tcp_buf.c |  12 ++++--
 udp.c     |  27 +++++++++----
 3 files changed, 99 insertions(+), 52 deletions(-)

diff --git a/tap.c b/tap.c
index f84cb0a..c730535 100644
--- a/tap.c
+++ b/tap.c
@@ -123,45 +123,6 @@ unsigned long tap_l2_max_len(const struct ctx *c)
 	return 0; /* Unreachable, for cppcheck's sake */
 }
 
-/**
- * tap_send_single() - Send a single frame
- * @c:		Execution context
- * @data:	Packet buffer
- * @l2len:	Total L2 packet length
- */
-void tap_send_single(const struct ctx *c, const void *data, size_t l2len)
-{
-	uint8_t padded[ETH_ZLEN] = { 0 };
-	struct iovec iov[2];
-	size_t iovcnt = 0;
-	uint32_t vnet_len;
-
-	if (l2len < ETH_ZLEN) {
-		memcpy(padded, data, l2len);
-		data = padded;
-		l2len = ETH_ZLEN;
-	}
-
-	vnet_len = htonl(l2len);
-
-	switch (c->mode) {
-	case MODE_PASST:
-		iov[iovcnt] = IOV_OF_LVALUE(vnet_len);
-		iovcnt++;
-		/* fall through */
-	case MODE_PASTA:
-		iov[iovcnt].iov_base = (void *)data;
-		iov[iovcnt].iov_len = l2len;
-		iovcnt++;
-
-		tap_send_frames(c, iov, iovcnt, 1);
-		break;
-	case MODE_VU:
-		vu_send_single(c, data, l2len);
-		break;
-	}
-}
-
 /**
  * tap_push_l2h() - Build an L2 header for an inbound packet
  * @c:		Execution context
@@ -497,6 +458,76 @@ static size_t tap_send_frames_passt(const struct ctx *c,
 	return i / bufs_per_frame;
 }
 
+/**
+ * tap_l2_offset() - Backend specific header size preceding each L2 frame
+ * @c:		Execution context
+ *
+ * Return: offset of the L2 frame within each frame's first buffer
+ */
+static size_t tap_l2_offset(const struct ctx *c)
+{
+	if (c->mode == MODE_PASST)
+		return sizeof(uint32_t);	/* vnet_len */
+
+	if (c->vhost.fd != -1)
+		return VNET_HLEN;
+
+	return 0;
+}
+
+/**
+ * tap_send_single() - Send a single frame
+ * @c:		Execution context
+ * @data:	Packet buffer
+ * @l2len:	Total L2 packet length
+ */
+void tap_send_single(const struct ctx *c, const void *data, size_t l2len)
+{
+	uint8_t padded[ETH_ZLEN] = { 0 };
+	struct iovec iov[2];
+	uint32_t vnet_len;
+	size_t iovcnt = 0;
+	size_t m = 0;
+
+	if (l2len < ETH_ZLEN) {
+		memcpy(padded, data, l2len);
+		data = padded;
+		l2len = ETH_ZLEN;
+	}
+
+	vnet_len = htonl(l2len);
+
+	switch (c->mode) {
+	case MODE_PASST:
+		/* create an iov for the length */
+		iov[iovcnt] = IOV_OF_LVALUE(vnet_len);
+		iovcnt++;
+		/* create the data iov */
+		iov[iovcnt].iov_base = (void *)data;
+		iov[iovcnt].iov_len = l2len;
+		iovcnt++;
+
+		m = tap_send_frames_passt(c, iov, iovcnt, 1);
+		break;
+	case MODE_PASTA:
+		/* don't create a length iov in the case of pasta */
+		iov[iovcnt].iov_base = (void *)data;
+		iov[iovcnt].iov_len = l2len;
+		iovcnt++;
+
+		m = tap_send_frames_pasta(c, iov, iovcnt, 1);
+		break;
+	case MODE_VU:
+		vu_send_single(c, data, l2len);
+		return;
+	}
+
+	if (!m)
+		debug("tap: failed to send a single frame");
+
+	pcap_multiple(iov, iovcnt, m, tap_l2_offset(c));
+}
+
 /**
  * tap_send_frames() - Send out multiple prepared frames
  * @c:			Execution context
@@ -537,8 +568,7 @@ size_t tap_send_frames(const struct ctx *c, const struct iovec *iov,
 		debug("tap: failed to send %zu frames of %zu",
 		      nframes - m, nframes);
 
-	pcap_multiple(iov, bufs_per_frame, m,
-		      c->mode == MODE_PASST ? sizeof(uint32_t) : 0);
+	pcap_multiple(iov, bufs_per_frame, m, tap_l2_offset(c));
 
 	return m;
 }
diff --git a/tcp_buf.c b/tcp_buf.c
index 1f28728..9cc541a 100644
--- a/tcp_buf.c
+++ b/tcp_buf.c
@@ -21,6 +21,7 @@
 #include <netinet/ip.h>
 
 #include <netinet/tcp.h>
+#include <linux/virtio_net.h>
 
 #include "util.h"
 #include "ip.h"
@@ -42,7 +43,7 @@
 /* Ethernet header for IPv4 and IPv6 frames */
 static struct ethhdr		tcp_eth_hdr[TCP_FRAMES_MEM];
 
-static struct tap_hdr		tcp_payload_tap_hdr[TCP_FRAMES_MEM];
+static struct virtio_net_hdr_mrg_rxbuf tcp_payload_tap_hdr[TCP_FRAMES_MEM];
 
 /* IP headers for IPv4 and IPv6 */
 static struct iphdr		tcp4_payload_ip[TCP_FRAMES_MEM];
@@ -107,7 +108,7 @@ void tcp_sock_iov_init(const struct ctx *c)
 	for (i = 0; i < TCP_FRAMES_MEM; i++) {
 		struct iovec *iov = tcp_l2_iov[i];
 
-		iov[TCP_IOV_TAP] = tap_hdr_iov(c, &tcp_payload_tap_hdr[i]);
+		iov[TCP_IOV_TAP] = tap_hdr_iov(c, (struct tap_hdr *)&tcp_payload_tap_hdr[i]);
 		iov[TCP_IOV_ETH].iov_len = sizeof(struct ethhdr);
 		iov[TCP_IOV_PAYLOAD].iov_base = &tcp_payload[i];
 		iov[TCP_IOV_ETH_PAD].iov_base = eth_pad;
@@ -210,7 +211,12 @@ static void tcp_l2_buf_fill_headers(const struct ctx *c,
 
 	l2len = tcp_fill_headers(c, conn, eh, ip4h, ip6h, th, &tail,
 				 iov_tail_size(&tail), csum_flags, seq);
-	tap_hdr_update(taph, l2len);
+
+	/* With vhost-net this buffer holds a virtio-net header, which a tap
+	 * one must not be written over
+	 */
+	if (c->vhost.fd == -1)
+		tap_hdr_update(taph, l2len);
 }
 
 /**
diff --git a/udp.c b/udp.c
index 5e3bd86..e360ccf 100644
--- a/udp.c
+++ b/udp.c
@@ -103,6 +103,7 @@
 #include <time.h>
 #include <arpa/inet.h>
 #include <linux/errqueue.h>
+#include <linux/virtio_net.h>
 
 #include "checksum.h"
 #include "util.h"
@@ -147,12 +148,16 @@ static struct ethhdr udp_eth_hdr[UDP_MAX_FRAMES];
  * struct udp_meta_t - Pre-cooked headers for UDP packets
  * @ip6h:	Pre-filled IPv6 header (except for payload_len and addresses)
  * @ip4h:	Pre-filled IPv4 header (except for tot_len and saddr)
- * @taph:	Tap backend specific header
+ * @vnet_hdr:	virtio-net header, used when sending through vhost-net
+ * @taph:	Tap backend specific header, used otherwise
  */
 static struct udp_meta_t {
 	struct ipv6hdr ip6h;
 	struct iphdr ip4h;
-	struct tap_hdr taph;
+	union {
+		struct virtio_net_hdr_mrg_rxbuf vnet_hdr;
+		struct tap_hdr taph;
+	};
 }
 #ifdef __AVX2__
 __attribute__ ((aligned(32)))
@@ -354,13 +359,15 @@ static void udp_tap_pad(struct iovec *iov)
 
 /**
  * udp_tap_prepare() - Convert one datagram into a tap frame
+ * @c:      Execution context
  * @mmh:	Receiving mmsghdr array
  * @idx:	Index of the datagram to prepare
  * @tap_omac:	MAC address of remote endpoint as seen from the guest
  * @toside:	Flowside for destination side
  * @no_udp_csum: Do not set UDP checksum
  */
-static void udp_tap_prepare(const struct mmsghdr *mmh,
+static void udp_tap_prepare(const struct ctx *c,
+				const struct mmsghdr *mmh,
 			    unsigned int idx,
 			    const uint8_t *tap_omac,
 			    const struct flowside *toside,
@@ -382,8 +389,10 @@ static void udp_tap_prepare(const struct mmsghdr *mmh,
 		udp_update_hdr6(&bm->ip6h, uh, &payload, toside,
 			        mmh[idx].msg_len, no_udp_csum);
 
-		l2len = MAX(l4len + sizeof(bm->ip6h) + ETH_HLEN, ETH_ZLEN);
-		tap_hdr_update(&bm->taph, l2len);
+		if (c->mode == MODE_PASST) {
+			l2len = MAX(l4len + sizeof(bm->ip6h) + ETH_HLEN, ETH_ZLEN);
+			tap_hdr_update(&bm->taph, l2len);
+		}
 
 		eh->h_proto = htons_constant(ETH_P_IPV6);
 		(*tap_iov)[UDP_IOV_IP] = IOV_OF_LVALUE(bm->ip6h);
@@ -391,8 +400,10 @@ static void udp_tap_prepare(const struct mmsghdr *mmh,
 		udp_update_hdr4(&bm->ip4h, uh, &payload, toside,
 			        mmh[idx].msg_len, no_udp_csum);
 
-		l2len = MAX(l4len + sizeof(bm->ip4h) + ETH_HLEN, ETH_ZLEN);
-		tap_hdr_update(&bm->taph, l2len);
+		if (c->mode == MODE_PASST) {
+			l2len = MAX(l4len + sizeof(bm->ip4h) + ETH_HLEN, ETH_ZLEN);
+			tap_hdr_update(&bm->taph, l2len);
+		}
 
 		eh->h_proto = htons_constant(ETH_P_IP);
 		(*tap_iov)[UDP_IOV_IP] = IOV_OF_LVALUE(bm->ip4h);
@@ -861,7 +872,7 @@ static void udp_buf_sock_to_tap(const struct ctx *c, int s, int n,
 		fwd_neigh_mac_get(c, &toside->oaddr, omac);
 
 	for (i = 0; i < n; i++)
-		udp_tap_prepare(udp_mh_recv, i, omac, toside, false);
+		udp_tap_prepare(c, udp_mh_recv, i, omac, toside, false);
 
 	tap_send_frames(c, &udp_l2_iov[0][0], UDP_NUM_IOVS, n);
 }
-- 
2.39.5 (Apple Git-154)


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 6/7] tap: Implement the pasta vhost-net to-guest path
  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-04 21:28 ` [PATCH 5/7] tap, tcp, udp: Prepare the to-guest path for vhost-net aerosouund
@ 2026-09-04 21:28 ` aerosouund
  2 siblings, 0 replies; 5+ messages in thread
From: aerosouund @ 2026-09-04 21:28 UTC (permalink / raw)
  To: passt-dev; +Cc: eperezma, Ammar Yasser

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 | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 142 insertions(+), 3 deletions(-)

diff --git a/tap.c b/tap.c
index c730535..0f51e62 100644
--- a/tap.c
+++ b/tap.c
@@ -359,12 +359,146 @@ 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 *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 (vhost_vq_state[1].last_used_idx != used_idx) {
+		uint16_t last_used, desc_id;
+
+		last_used = vhost_vq_state[1].last_used_idx % VHOST_NDESCS;
+		desc_id = le32toh(used->ring[last_used].id);
+
+		for (;;) {
+			/* keep going until we find a descriptor without the
+			 * next flag
+			 */
+			vhost_vq_state[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);
+		}
+
+		vhost_vq_state[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
+ * @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 *avail = &vring_avail_all[1].avail;
+	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[1].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[1].num_free < bufs_per_frame)
+			break;
+
+		/* set the index of the avail ring in the tx queue to be our
+		 * last_used_idx
+		 */
+		head = vhost_vq_state[1].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[1].num_free -= bufs_per_frame;
+
+		for (j = 0; j < bufs_per_frame; ++j) {
+			uint16_t next = vhost_vq_state[1].next_free %
+					VHOST_NDESCS;
+			/* get the last_used_idx descriptor */
+			struct vring_desc *desc = &vring_desc[1][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.
+			 */
+			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);
+
+			vhost_vq_state[1].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);
+
+	vhost_kick(&vring_used_all[1].used, c->vhost.vq[1].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 +509,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 +653,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 +691,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)


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 1/7] tap: Move the tap_hdr file to a separate file
  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
  0 siblings, 0 replies; 5+ messages in thread
From: David Gibson @ 2026-09-05  1:52 UTC (permalink / raw)
  To: aerosouund; +Cc: passt-dev, eperezma

[-- Attachment #1: Type: text/plain, Size: 2615 bytes --]

On Sat, Sep 05, 2026 at 12:28:20AM +0300, aerosouund wrote:
> From: Ammar Yasser <aerosound161@gmail.com>
> 
> It was defined in tap.h, put it on its own so that future callers won't
> need to depend on all definitions in tap.h.
> Also turn it into a union of a vnet_len and virtio_net_mrg_rxbuf
> because for vhost acceleration the frames will have this virtio net
> header prepended to them.
> 
> Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
> Signed-off-by: Ammar Yasser <aerosound161@gmail.com>

The code motion itself looks fine.


> ---
>  tap.h     |  9 +--------
>  tap_hdr.h | 23 +++++++++++++++++++++++
>  2 files changed, 24 insertions(+), 8 deletions(-)
>  create mode 100644 tap_hdr.h
> 
> diff --git a/tap.h b/tap.h
> index b335933..1625975 100644
> --- a/tap.h
> +++ b/tap.h
> @@ -10,6 +10,7 @@
>  #include <stdint.h>
>  
>  #include "passt.h"
> +#include "tap_hdr.h"
>  
>  /** L2_MAX_LEN_PASTA - Maximum frame length for pasta mode (with L2 header)
>   *
> @@ -38,14 +39,6 @@
>  
>  struct udphdr;
>  
> -/**
> - * struct tap_hdr - tap backend specific headers
> - * @vnet_len:	Frame length (for qemu socket transport)
> - */
> -struct tap_hdr {
> -	uint32_t vnet_len;
> -} __attribute__((packed));
> -
>  /**
>   * tap_hdr_iov() - struct iovec for a tap header
>   * @c:		Execution context
> diff --git a/tap_hdr.h b/tap_hdr.h
> new file mode 100644
> index 0000000..aa270b7
> --- /dev/null
> +++ b/tap_hdr.h
> @@ -0,0 +1,23 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later
> + * Copyright (c) 2021 Red Hat GmbH
> + * Author: Stefano Brivio <sbrivio@redhat.com>

This might be a bit pedantic since this may be a smaller block of code
than is copyrightable, but..

Fwiw, Red Hat legal's advice, last I knew was to prefer simply
"Copyright Red Hat" - the rest is apparently not really useful.  Also,
these specific lines were added by me in 2023, not Stefano in 2021
(commit 4b3d38a06).

> + */
> +
> +#ifndef TAP_HDR_H
> +#define TAP_HDR_H
> +
> +#include <stdint.h>
> +#include <linux/virtio_net.h>
> +
> +/**
> + * struct tap_hdr - tap backend specific headers
> + * @vnet_len:	Frame length (for qemu socket transport)
> + */
> +struct tap_hdr {
> +	union {
> +		uint32_t vnet_len;
> +		struct virtio_net_hdr_mrg_rxbuf hdr;
> +	};
> +};
> +
> +#endif /* TAP_HDR_H */
> -- 
> 2.39.5 (Apple Git-154)
> 

-- 
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 --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-09-05  1:53 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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 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

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).