public inbox for passt-dev@passt.top
 help / color / mirror / code / Atom feed
* [PATCH v2 2/7] conf: Add context fields, epoll types and --vhost-kernel flag to pasta
@ 2026-10-16 20:36 Ammar Yasser
  0 siblings, 0 replies; 2+ messages in thread
From: Ammar Yasser @ 2026-10-16 20:36 UTC (permalink / raw)
  To: passt-dev; +Cc: Eugenio Pérez

--vhost-kernel controls whether to use vhost-net acceleration for pasta.
It takes one of three modes: "on" requires acceleration and fails if it
isn't available, "off" never uses it, and "auto" uses it if it's
available and falls back to plain tap operation otherwise. "auto" is the
default, so an unmodified invocation picks up acceleration wherever the
kernel offers it, and behaves as before where it doesn't.

EPOLL_TYPE_VHOST_CALL means the kernel wants to notify us about data it
has written. EPOLL_TYPE_VHOST_ERROR means the kernel encountered an
internal error on vhost and wants to tell us something went wrong.

Collect the vhost state in a single struct vhost_ctx with the following
fields:

  - mode:     which of the three modes above was requested
  - fd:       file descriptor for /dev/vhost-net, which doubles as our
              indication that setup succeeded
  - features: virtio feature bits negotiated with vhost-net
  - vq[]:     kick, call and err eventfds for both queues

Modify tap_hdr_iov to create an iov with the size of a
virtio_net_mrg_rxbuf if vhost initialization has been successful (we are
in mode 'on' or 'auto' and we didn't need the tap fallback)

Move the L2_MAX_LEN_XX constants from tap.h to passt.h to avoid the
circular dependency between both files. VHOST_DESC_BYTES is defined as
the maximum l2 frame size + the size of the virtio net header, if the
length constant remains in tap.h passt.h would need to include it, but
tap.h needs to include passt.h to get the definition of `struct ctx`.
Moving the length constants to passt.h makes it free of dependency on
tap.h

Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
Signed-off-by: Ammar Yasser <aerosound161@gmail.com>
---
 conf.c       | 19 +++++++++++++
 epoll_type.h |  4 +++
 passt.c      |  3 ++
 passt.h      | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 tap.h        | 36 ++++++------------------
 5 files changed, 112 insertions(+), 29 deletions(-)

diff --git a/conf.c b/conf.c
index 149a4c7..fe578fc 100644
--- a/conf.c
+++ b/conf.c
@@ -736,6 +736,9 @@ pasta_opts:
 		"    default: auto\n"
 		"  --host-lo-to-ns-lo	Translate host-loopback forwards to\n"
 		"			namespace loopback\n"
+		"  --vhost-kernel[=MODE]	Use vhost-kernel acceleration\n"
+		"    MODE is \"on\", \"off\" or \"auto\"\n"
+		"    default: auto\n"
 		"  --userns NSPATH 	Target user namespace to join\n"
 		"  --netns PATH|NAME	Target network namespace to join\n"
 		"  --netns-only		Don't join existing user namespace\n"
@@ -767,6 +770,7 @@ enum passt_modes conf_mode(int argc, char *argv[])
 	int vhost_user = 0;
 	const struct option optvu[] = {
 		{"vhost-user",	no_argument,		&vhost_user,	1 },
+		{"vhost-kernel", optional_argument,	NULL,		0 },
 		{ 0 },
 	};
 	char argv0[PATH_MAX], *basearg0;
@@ -1317,6 +1321,7 @@ void conf(struct ctx *c, int argc, char **argv)
 		{"conf-path",	required_argument,	NULL,		'c' },
 		{"chroot-fallback", no_argument,	NULL, 		32 },
 		{"no-pidns",	no_argument,		NULL,		33 },
+		{"vhost-kernel", optional_argument,	NULL,		34 },
 		{ 0 },
 	};
 	const char *optstring = "+dqfel:hs:c:F:I:p:P:m:a:n:M:g:i:o:D:S:H:461t:u:T:U:";
@@ -1566,6 +1571,20 @@ void conf(struct ctx *c, int argc, char **argv)
 				die("--no-pidns is for pasta mode only");

 			c->no_pidns = true;
+			break;
+		case 34:
+			if (c->mode != MODE_PASTA)
+				die("--vhost-kernel is for pasta mode only");
+
+			if (!optarg || !strcmp(optarg, "auto"))
+				c->vhost.mode = VHOST_MODE_AUTO;
+			else if (!strcmp(optarg, "on"))
+				c->vhost.mode = VHOST_MODE_ON;
+			else if (!strcmp(optarg, "off"))
+				c->vhost.mode = VHOST_MODE_OFF;
+			else
+				die("Invalid vhost-kernel mode: %s", optarg);
+
 			break;
 		case 'd':
 			c->debug = 1;
diff --git a/epoll_type.h b/epoll_type.h
index 061325a..e3206d1 100644
--- a/epoll_type.h
+++ b/epoll_type.h
@@ -50,6 +50,10 @@ enum epoll_type {
 	EPOLL_TYPE_CONF_LISTEN,
 	/* Configuration socket */
 	EPOLL_TYPE_CONF,
+	/* vhost-kernel call socket */
+	EPOLL_TYPE_VHOST_CALL,
+	/* vhost-kernel error socket */
+	EPOLL_TYPE_VHOST_ERROR,

 	EPOLL_NUM_TYPES,
 };
diff --git a/passt.c b/passt.c
index 5054551..6002231 100644
--- a/passt.c
+++ b/passt.c
@@ -65,6 +65,7 @@ char pkt_buf[PKT_BUF_BYTES]	__attribute__ ((aligned(PAGE_SIZE)));
 struct ctx passt_ctx = {
 	.pidfile_fd		= -1,
 	.fd_tap			= -1,
+	.vhost			= { .fd = -1 },
 	.fd_tap_listen		= -1,
 	.fd_control_listen	= -1,
 	.fd_repair_listen	= -1,
@@ -92,6 +93,8 @@ char *epoll_type_str[] = {
 	[EPOLL_TYPE_NL_NEIGH]		= "netlink neighbour notifier socket",
 	[EPOLL_TYPE_CONF_LISTEN]	= "configuration listening socket",
 	[EPOLL_TYPE_CONF]		= "configuration socket",
+	[EPOLL_TYPE_VHOST_CALL]		= "vhost-kernel call socket",
+	[EPOLL_TYPE_VHOST_ERROR]	= "vhost-kernel error socket",
 };
 static_assert(ARRAY_SIZE(epoll_type_str) == EPOLL_NUM_TYPES,
 	      "epoll_type_str[] doesn't match enum epoll_type");
diff --git a/passt.h b/passt.h
index afd8e9f..be68002 100644
--- a/passt.h
+++ b/passt.h
@@ -13,8 +13,11 @@ union epoll_ref;

 #include <stdbool.h>
 #include <assert.h>
+#include <limits.h>
 #include <sys/epoll.h>

+#include <linux/virtio_net.h>
+
 #include "pif.h"
 #include "packet.h"
 #include "siphash.h"
@@ -35,8 +38,43 @@ union epoll_ref;
 #define MAC_OUR_LAA	\
 	((uint8_t [ETH_ALEN]){0x9a, 0x55, 0x9a, 0x55, 0x9a, 0x55})

-/* Large enough for ~128 maximum size frames */
-#define PKT_BUF_BYTES		(8UL << 20)
+
+/** L2_MAX_LEN_PASTA - Maximum frame length for pasta mode (with L2 header)
+ *
+ * The kernel tuntap device imposes a maximum frame size of 65535 including
+ * 'hard_header_len' (14 bytes for L2 Ethernet in the case of "tap" mode).
+ */
+#define L2_MAX_LEN_PASTA	USHRT_MAX
+
+/** L2_MAX_LEN_PASST - Maximum frame length for passt mode (with L2 header)
+ *
+ * The only structural limit the QEMU socket protocol imposes on frames is
+ * (2^32-1) bytes, but that would be ludicrously long in practice.  For now,
+ * limit it somewhat arbitrarily to 65535 bytes.  FIXME: Work out an appropriate
+ * limit with more precision.
+ */
+#define L2_MAX_LEN_PASST	USHRT_MAX
+
+/** L2_MAX_LEN_VU - Maximum frame length for vhost-user mode (with L2 header)
+ *
+ * vhost-user allows multiple buffers per frame, each of which can be quite
+ * large, so the inherent frame size limit is rather large.  Much larger than is
+ * actually useful for IP.  For now limit arbitrarily to 65535 bytes. FIXME:
+ * Work out an appropriate limit with more precision.
+ */
+#define L2_MAX_LEN_VU		USHRT_MAX
+
+/* Number of descriptors in each vhost-net virtqueue */
+#define VHOST_NDESCS		128
+
+/* Bytes of pkt_buf backing one from-guest descriptor: a maximum size frame
+ * plus the virtio-net header the kernel writes in front of it
+ */
+#define VHOST_DESC_BYTES	(L2_MAX_LEN_PASTA +			\
+				 sizeof(struct virtio_net_hdr_mrg_rxbuf))
+
+/* One maximum size frame per vhost-net descriptor */
+#define PKT_BUF_BYTES		(VHOST_NDESCS * VHOST_DESC_BYTES)

 extern char pkt_buf		[PKT_BUF_BYTES];

@@ -156,6 +194,40 @@ struct ip6_ctx {

 #include <netinet/if_ether.h>

+/**
+ * enum vhost_mode - Whether to use vhost-kernel acceleration
+ * @VHOST_MODE_AUTO:	Use it if it's available, fall back to plain tap if not
+ * @VHOST_MODE_ON:	Require it, fail if it's not available
+ * @VHOST_MODE_OFF:	Never use it
+ */
+enum vhost_mode {
+	VHOST_MODE_AUTO = 0,
+	VHOST_MODE_ON,
+	VHOST_MODE_OFF,
+};
+
+/**
+ * struct vhost_ctx - Execution context for vhost-kernel acceleration
+ * @mode:	Whether to use acceleration at all, see enum vhost_mode
+ * @fd:		File descriptor for /dev/vhost-net, -1 if not set up
+ * @features:	virtio feature bits negotiated with vhost-net
+ * @vq:		Per-virtqueue eventfds ([0] is from-guest, [1] is to-guest)
+ * @vq.kick_fd:	Written by us, to tell the kernel we queued something
+ * @vq.call_fd:	Written by the kernel, to tell us it queued something
+ * @vq.err_fd:	Written by the kernel on an internal vhost error
+ */
+struct vhost_ctx {
+	enum vhost_mode mode;
+	int fd;
+	uint64_t features;
+
+	struct {
+		int kick_fd;
+		int call_fd;
+		int err_fd;
+	} vq[2];
+};
+
 /**
  * struct ctx - Execution context
  * @mode:		Operation mode, qemu/UNIX domain socket or namespace/tap
@@ -185,6 +257,7 @@ struct ip6_ctx {
  * @our_tap_mac:	Pasta/passt's MAC on the tap link
  * @guest_mac:		MAC address of guest or namespace, seen or configured
  * @hash_secret:	128-bit secret for siphash functions
+ * @vhost:		vhost-kernel acceleration context, pasta mode only
  * @ifi4:		Template interface for IPv4, -1: none, 0: IPv4 disabled
  * @ip4:		IPv4 configuration
  * @dns_search:		DNS search list
@@ -265,6 +338,8 @@ struct ctx {

 	uint64_t hash_secret[2];

+	struct vhost_ctx vhost;
+
 	int ifi4;
 	struct ip4_ctx ip4;

diff --git a/tap.h b/tap.h
index 1625975..f8c30e2 100644
--- a/tap.h
+++ b/tap.h
@@ -12,31 +12,6 @@
 #include "passt.h"
 #include "tap_hdr.h"

-/** L2_MAX_LEN_PASTA - Maximum frame length for pasta mode (with L2 header)
- *
- * The kernel tuntap device imposes a maximum frame size of 65535 including
- * 'hard_header_len' (14 bytes for L2 Ethernet in the case of "tap" mode).
- */
-#define L2_MAX_LEN_PASTA	USHRT_MAX
-
-/** L2_MAX_LEN_PASST - Maximum frame length for passt mode (with L2 header)
- *
- * The only structural limit the QEMU socket protocol imposes on frames is
- * (2^32-1) bytes, but that would be ludicrously long in practice.  For now,
- * limit it somewhat arbitrarily to 65535 bytes.  FIXME: Work out an appropriate
- * limit with more precision.
- */
-#define L2_MAX_LEN_PASST	USHRT_MAX
-
-/** L2_MAX_LEN_VU - Maximum frame length for vhost-user mode (with L2 header)
- *
- * vhost-user allows multiple buffers per frame, each of which can be quite
- * large, so the inherent frame size limit is rather large.  Much larger than is
- * actually useful for IP.  For now limit arbitrarily to 65535 bytes. FIXME:
- * Work out an appropriate limit with more precision.
- */
-#define L2_MAX_LEN_VU		USHRT_MAX
-
 struct udphdr;

 /**
@@ -50,10 +25,17 @@ struct udphdr;
 static inline struct iovec tap_hdr_iov(const struct ctx *c,
 				       struct tap_hdr *thdr)
 {
-	return (struct iovec){
+	struct iovec ret = {
 		.iov_base = thdr,
-		.iov_len = c->mode == MODE_PASST ? sizeof(*thdr) : 0,
 	};
+
+	if (c->vhost.fd != -1) {
+		ret.iov_len = sizeof(struct virtio_net_hdr_mrg_rxbuf);
+	} else {
+		ret.iov_len = c->mode == MODE_PASST ? sizeof(thdr->vnet_len) : 0;
+	}
+
+	return ret;
 }

 /**
--
2.39.5 (Apple Git-154)



^ permalink raw reply	[flat|nested] 2+ messages in thread
* [PATCH v2 0/7] Add vhost-net kernel support to pasta
@ 2026-10-16 20:49 aerosouund
  2026-10-16 20:49 ` [PATCH v2 2/7] conf: Add context fields, epoll types and --vhost-kernel flag " aerosouund
  0 siblings, 1 reply; 2+ messages in thread
From: aerosouund @ 2026-10-16 20:49 UTC (permalink / raw)
  To: aerosound161, passt-dev

From: Ammar Yasser <aerosound161@gmail.com>

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

v2:
- removed all use of epoll_ctl in vhost_setup_eventfds in favor of epoll_add
- prevent all functions that register memory regions into the vhost_memory
  object from overflowing
- remove the unions with flexible array members in vring_avail_pasta and
  vring_used_pasta in favor of hardcoding the ring array to have VHOST_NDESCS
  entries to avoid having to ignore Wpedantic
- remove the && operator with TCP_CURR_IDX when computing no_csum
- make desc_id in tx_reap a uint32_t
- don't make vhost-kernel a required argument

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        | 368 +++++++++++++++++++++++++++++++++++++++++++++------
 tap.h        |  47 ++-----
 tap_hdr.h    |  23 ++++
 tcp_buf.c    | 149 ++++++++++++++++-----
 tcp_buf.h    |   4 +
 udp.c        |  47 +++++--
 udp.h        |   3 +
 util.c       |  18 +++
 util.h       |   2 +
 vhost.c      | 295 +++++++++++++++++++++++++++++++++++++++++
 vhost.h      |  91 +++++++++++++
 16 files changed, 1036 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] 2+ messages in thread

end of thread, other threads:[~2026-10-16 20:56 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-16 20:36 [PATCH v2 2/7] conf: Add context fields, epoll types and --vhost-kernel flag to pasta Ammar Yasser
2026-10-16 20:49 [PATCH v2 0/7] Add vhost-net kernel support " aerosouund
2026-10-16 20:49 ` [PATCH v2 2/7] conf: Add context fields, epoll types and --vhost-kernel flag " 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).