From: Eugenio Perez Martin <eperezma@redhat.com>
To: David Gibson <david@gibson.dropbear.id.au>
Cc: passt-dev@passt.top, jasowang@redhat.com
Subject: Re: [RFC v2 01/11] tap: implement vhost_call_cb
Date: Mon, 28 Jul 2025 18:33:43 +0200 [thread overview]
Message-ID: <CAJaqyWc42NLNU5anS=G4Uf52v2e-ZOVix5CFt3Wg=adns32QPw@mail.gmail.com> (raw)
In-Reply-To: <aICHsmIC0GrUQbnC@zatzit>
On Wed, Jul 23, 2025 at 8:57 AM David Gibson
<david@gibson.dropbear.id.au> wrote:
>
> On Wed, Jul 09, 2025 at 07:47:38PM +0200, Eugenio Pérez wrote:
> > Implement the rx side of the vhost-kernel, where the namespace send
> > packets through its tap device and the kernel passes them to
> > pasta using a virtqueue. The virtqueue is build using a
> > virtual ring (vring), which includes:
> > * A descriptor table (buffer info)
> > * An available ring (buffers ready for the kernel)
> > * A used ring (buffers that the kernel has filled)
> >
> > The descriptor table holds an array of buffers defined by address and
> > length. The kernel writes the packets transmitted by the namespace into
> > these buffers. The number of descriptors (vq size) is set by
> > VHOST_NDESCS. Pasta fills this table using pkt_buf, splitting it
> > evenly across all descriptors. This table is read-only for the kernel.
> >
> > The available ring is where pasta marks which buffers the kernel can
> > use. It's read only for the kernel. It includes a ring[] array with
> > the descriptor indexes and an avail->idx index. Pasta increments
> > avail->idx when a new buffer is added, modulo the size of the
> > virtqueue. As pasta writes the rx buffers sequentially, ring[] is
> > always [0, 1, 2...] and only avail->idx is incremented when new buffers
> > are available for the kernel. avail->idx can be incremented by more
> > than one at a time.
> >
> > Pasta also notifies the kernel of new available buffers by writing to
> > the kick eventfd.
> >
> > Once the kernel has written a frame in a descriptor it writes its index
> > into used_ring->ring[] and increments the used_ring->idx accordly.
> > Like the avail idx the kernel can increase it by more than one. Pasta
> > gets a notification in the call eventfd, so we add it into the epoll ctx.
> >
> > Pasta assumes buffers are used in order. QEMU also assumes it in the
> > virtio-net migration code so it is safe.
> >
> > Now, vhost-kernel is designed to read the virtqueues and the buffers as
> > *guest* physical addresses (GPA), not process virtual addresses (HVA).
> > The way QEMU tells the translations is through the memory regions.
> > Since we don't have GPAs, let's just send the memory regions as a 1:1
> > translations of the HVA.
> >
> > TODO: Evaluate if we can reuse the tap fd code instead of making a new
> > epoll event type.
>
> I'm not sure this is a particularly desirable code, unless it obvious
> removes a bunch of duplicated code. As a rule I'd prefer to use
> different epoll event types rather than a single epoll type that then
> needs to consult additional information to determine how to handle it.
>
Sure, it can be done that way.
> > TODO: Split a new file for vhost (Stefano)
> >
> > Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
> > ---
> > RFC v2:
> > * Need to integrate "now" parameter in tap_add_packet and replace
> > TAP_MSGS to TAP_MSGS{4,6}.
> > * Actually refill rx queue at the end of operation.
> > * Explain virtqueues and memory regions theory of operation.
> > * Extrack vhost_kick so it can be reused by tx.
> > * Add macro for VHOST regions.
> > * Only register call_cb in rx queue, as tx calls are handled just if
> > needed.
> > * Renamed vhost_call_cb to tap_vhost_input (David)
> > * Move the inuse and last_used_idx to a writable struct from vhost tx
> > function. Context is readonly for vhost tx code path.
> > * Changed from inuse to num_free tracking, more aligned with kernel drv
> > * Use always the same tap_pool instead of having the two splitted for v4
> > and v6.
> > * Space between (struct vhost_memory_region) {.XXX = ...}. (Stefano)
> > * Expand comments about memory region size (David)
> > * Add some TODOs.
> >
> > Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
> > ---
> > epoll_type.h | 2 +
> > passt.c | 7 +-
> > passt.h | 10 +-
> > tap.c | 311 ++++++++++++++++++++++++++++++++++++++++++++++++++-
> > tap.h | 8 ++
> > 5 files changed, 335 insertions(+), 3 deletions(-)
> >
> > diff --git a/epoll_type.h b/epoll_type.h
> > index 12ac59b..0371c14 100644
> > --- a/epoll_type.h
> > +++ b/epoll_type.h
> > @@ -44,6 +44,8 @@ enum epoll_type {
> > EPOLL_TYPE_REPAIR_LISTEN,
> > /* TCP_REPAIR helper socket */
> > EPOLL_TYPE_REPAIR,
> > + /* vhost-kernel call socket */
> > + EPOLL_TYPE_VHOST_CALL,
> >
> > EPOLL_NUM_TYPES,
> > };
> > diff --git a/passt.c b/passt.c
> > index 388d10f..0f2659c 100644
> > --- a/passt.c
> > +++ b/passt.c
> > @@ -79,6 +79,7 @@ char *epoll_type_str[] = {
> > [EPOLL_TYPE_VHOST_KICK] = "vhost-user kick socket",
> > [EPOLL_TYPE_REPAIR_LISTEN] = "TCP_REPAIR helper listening socket",
> > [EPOLL_TYPE_REPAIR] = "TCP_REPAIR helper socket",
> > + [EPOLL_TYPE_VHOST_CALL] = "vhost-kernel call socket",
> > };
> > static_assert(ARRAY_SIZE(epoll_type_str) == EPOLL_NUM_TYPES,
> > "epoll_type_str[] doesn't match enum epoll_type");
> > @@ -310,7 +311,8 @@ loop:
> >
> > switch (ref.type) {
> > case EPOLL_TYPE_TAP_PASTA:
> > - tap_handler_pasta(&c, eventmask, &now);
> > + // TODO: Find a better way, maybe reuse the fd.
> > + // tap_handler_pasta(&c, eventmask, &now);
>
> This change seems bogus. We still need this if we want to be able to
> fall back to tap without vhost, which I think we want to be able to
> do. I'm also not clear why you need it - if you don't want these
> events, can't you just not register them (at least not with this event
> type) in epoll_ctl?
>
Absolutely, it was commented out for testing purposes but I forgot to note it.
> > break;
> > case EPOLL_TYPE_TAP_PASST:
> > tap_handler_passt(&c, eventmask, &now);
> > @@ -357,6 +359,9 @@ loop:
> > case EPOLL_TYPE_REPAIR:
> > repair_handler(&c, eventmask);
> > break;
> > + case EPOLL_TYPE_VHOST_CALL:
> > + tap_vhost_input(&c, ref, &now);
>
> I'd suggest calling this tap_handler_vhost() for consistency with
> tap_handler_pasta() and others. I've also found it usually ends up a
> bit cleaner to pass the relevant individual members of ref, rather
> than the whole thing.
>
I'm ok with tap_handler_vhost, but tap_vhost_input was your suggestion :) [1].
> > + break;
> > default:
> > /* Can't happen */
> > ASSERT(0);
> > diff --git a/passt.h b/passt.h
> > index 8693794..7bb86c4 100644
> > --- a/passt.h
> > +++ b/passt.h
> > @@ -45,7 +45,7 @@ union epoll_ref;
> > * @icmp: ICMP-specific reference part
> > * @data: Data handled by protocol handlers
> > * @nsdir_fd: netns dirfd for fallback timer checking if namespace is gone
> > - * @queue: vhost-user queue index for this fd
> > + * @queue: vhost queue index for this fd
> > * @u64: Opaque reference for epoll_ctl() and epoll_wait()
> > */
> > union epoll_ref {
> > @@ -269,11 +269,14 @@ struct ctx {
> > int fd_tap;
> > int fd_repair_listen;
> > int fd_repair;
> > + /* TODO document all added fields */
> > + int fd_vhost;
> > unsigned char our_tap_mac[ETH_ALEN];
> > unsigned char guest_mac[ETH_ALEN];
> > uint16_t mtu;
> >
> > uint64_t hash_secret[2];
> > + uint64_t virtio_features;
> >
> > int ifi4;
> > struct ip4_ctx ip4;
> > @@ -297,6 +300,11 @@ struct ctx {
> > int no_icmp;
> > struct icmp_ctx icmp;
> >
> > + struct {
> > + int kick_fd;
> > + int call_fd;
> > + } vq[2];
>
> Maybe not in scope for this series, but this is reminding me that we
> should really split out the tap-backend specific fields into their own
> union of substructures to make it clearer what fields are relevant in
> which configurations.
>
>
> > int no_dns;
> > int no_dns_search;
> > int no_dhcp_dns;
> > diff --git a/tap.c b/tap.c
> > index 6db5d88..e4a3822 100644
> > --- a/tap.c
> > +++ b/tap.c
> > @@ -31,6 +31,7 @@
> > #include <sys/types.h>
> > #include <sys/stat.h>
> > #include <fcntl.h>
> > +#include <sys/eventfd.h>
> > #include <sys/uio.h>
> > #include <stdbool.h>
> > #include <stdlib.h>
> > @@ -101,6 +102,51 @@ static PACKET_POOL_NOINIT(pool_tap6, TAP_MSGS_IP6, pkt_buf);
> > #define TAP_SEQS 128 /* Different L4 tuples in one batch */
> > #define FRAGMENT_MSG_RATE 10 /* # seconds between fragment warnings */
> >
> > +#define VHOST_VIRTIO 0xAF
> > +#define VHOST_GET_FEATURES _IOR(VHOST_VIRTIO, 0x00, __u64)
> > +#define VHOST_SET_FEATURES _IOW(VHOST_VIRTIO, 0x00, __u64)
> > +#define VHOST_SET_OWNER _IO(VHOST_VIRTIO, 0x01)
> > +#define VHOST_SET_MEM_TABLE _IOW(VHOST_VIRTIO, 0x03, struct vhost_memory)
> > +#define VHOST_SET_VRING_NUM _IOW(VHOST_VIRTIO, 0x10, struct vhost_vring_state)
> > +#define VHOST_SET_VRING_ADDR _IOW(VHOST_VIRTIO, 0x11, struct vhost_vring_addr)
> > +#define VHOST_SET_VRING_KICK _IOW(VHOST_VIRTIO, 0x20, struct vhost_vring_file)
> > +#define VHOST_SET_VRING_CALL _IOW(VHOST_VIRTIO, 0x21, struct vhost_vring_file)
> > +#define VHOST_SET_VRING_ERR _IOW(VHOST_VIRTIO, 0x22, struct vhost_vring_file)
> > +#define VHOST_SET_BACKEND_FEATURES _IOW(VHOST_VIRTIO, 0x25, __u64)
> > +#define VHOST_NET_SET_BACKEND _IOW(VHOST_VIRTIO, 0x30, struct vhost_vring_file)
>
> This stuff probably belongs in linux_dep.h (if you can't get it from a
> system or kernel header).
>
> > +#define VHOST_NDESCS (PKT_BUF_BYTES / 65520)
>
> I suspect this is wrong. For starters we should definitely be using a
> define, not open-coded 65520. But more importantly, 65520 is the
> (default) MTU - the maximum size *of the IP packet*, excluding L2
> headers. I'm assuming these buffers will also need to contain the L2
> header, so will need to be larger. Looking at the later code, it
> looks like these buffers also include the virtio_net_hdr_mrg_rxbuf
> pseudo-physical header, so will need to be bigger again.
>
> I think you'll want a new define for this, which would be
> L2_MAX_LEN_PASTA plus whatever you need on top of the Ethernet header
> (virtion_net_hdr_mrg_rxbuf, AFAICT). For now, I'm going to call this
> L1_MAX_LEN_VHOST, which is not a great name, but I want to refer to it
> later in this mail. You may also need/want to round it up to
> 4/8/whatever bytes to avoid poorly aligned buffers.
>
Very good points, applying it for the next series.
> > +static_assert(!(VHOST_NDESCS & (VHOST_NDESCS - 1)),
> > + "Number of vhost descs must be a power of two by standard");
>
> Relying on this calculation come out to a power of 2 seems kind of
> fragile. Maybe just round down to a power of 2 instead? Although I
> guess that could mean a bunch of wasted space.
>
To me it's the classical kernel include/linux/log2.h:is_power_of_2
function, but we can replace (or entirely omit) the assert for sure.
> To avoid wasting space if this doesn't come neatly to a power of 2,
> would it be safe to round _up_ to a power of 2, as long as we never,
> ever put an index beyond what we actually have space for into the
> available queue?
>
All descriptors are exposed to the kernel always in the rx queue, so
rounding is not possible. This number must be a power of 2 in the
split case, but I'll be happy if it is just a comment in the final
version.
> > +static struct {
> > + /* Number of free descriptors */
> > + uint16_t num_free;
> > +
> > + /* Last used idx processed */
> > + uint16_t last_used_idx;
> > +} vqs[2];
> > +
> > +static struct vring_desc vring_desc[2][VHOST_NDESCS] __attribute__((aligned(PAGE_SIZE)));
> > +static union {
> > + struct vring_avail avail;
> > + char buf[offsetof(struct vring_avail, ring[VHOST_NDESCS])];
>
> The purpose of this idiom of a union with a char buffer based on
> offsetof isn't obvious to me yet.
>
It's because vring_avail contains a flexible array member.
In VirtIO the avail ring is defined as:
struct virtq_avail {
le16 flags;
le16 idx;
le16 ring[ /* Queue Size */ ];
/* ommiting last member here, as it is not used by pasta */
};
But the kernel uapi definition of vring_avail is:
struct vring_avail {
__virtio16 flags;
__virtio16 idx;
__virtio16 ring[];
};
So I need to allocate enough bytes for ring[]. To me the easiest way
is by using char buf[offsetof(last_member_of_ring[])].
I would be happy to redefine it for pasta, as we know the size in
advance, or to hide it in a macro.
> > +} vring_avail_0 __attribute__((aligned(PAGE_SIZE))), vring_avail_1 __attribute__((aligned(PAGE_SIZE)));
> > +static union {
> > + struct vring_used used;
> > + char buf[offsetof(struct vring_used, ring[VHOST_NDESCS])];
> > +} vring_used_0 __attribute__((aligned(PAGE_SIZE))), vring_used_1 __attribute__((aligned(PAGE_SIZE)));
>
> Maybe 2 entry arrays, rather than *_0 and *_1?
>
Flexible array members again :(.
> > +
> > +/* all descs ring + 2rings * 2vqs + tx pkt buf + rx pkt buf */
> > +#define N_VHOST_REGIONS 6
> > +union {
> > + struct vhost_memory mem;
> > + char buf[offsetof(struct vhost_memory, regions[N_VHOST_REGIONS])];
> > +} vhost_memory = {
> > + .mem = {
> > + .nregions = N_VHOST_REGIONS,
> > + },
> > +};
> > +
> > /**
> > * tap_l2_max_len() - Maximum frame size (including L2 header) for current mode
> > * @c: Execution context
> > @@ -399,6 +445,18 @@ void tap_icmp6_send(const struct ctx *c,
> > tap_send_single(c, buf, l4len + ((char *)icmp6h - buf));
> > }
> >
> > +static void vhost_kick(struct vring_used *used, int kick_fd) {
>
> passt style puts the opening { of a function on the nect line.
>
Ouch, I'll fix it in the next version!
> > + /* We need to expose available array entries before checking avail
> > + * event.
> > + *
> > + * TODO: Does eventfd_write already do this?
> > + */
> > + smp_mb();
> > +
> > + if (!(used->flags & VRING_USED_F_NO_NOTIFY))
> > + eventfd_write(kick_fd, 1);
> > +}
> > +
> > /**
> > * tap_send_frames_pasta() - Send multiple frames to the pasta tap
> > * @c: Execution context
> > @@ -1386,6 +1444,89 @@ void tap_listen_handler(struct ctx *c, uint32_t events)
> > tap_start_connection(c);
> > }
> >
> > +static void *virtqueue_get_rx_buf(unsigned qid, unsigned *len)
>
> None of these functions have descriptive comment blocks yet. I
> realise this is an RFC, but it can certainly make things easier to
> review if you know what to expect before reading the function.
>
> > +{
> > + struct vring_used *used = !qid ? &vring_used_0.used : &vring_used_1.used;
>
> Using an array as suggested above would avoid this awkward construct.
>
> > + uint32_t i;
> > + uint16_t used_idx, last_used;
>
> passt style orders declarations in reverse order of line length (when
> possible), so this line should be before the previous one.
>
Got it, I'll use it from now on.
> > +
> > + /* TODO think if this has races with previous eventfd_read */
> > + /* TODO we could improve performance with a shadow_used_idx */
> > + used_idx = le16toh(used->idx);
> > +
> > + smp_rmb();
> > +
> > + if (used_idx == vqs[0].last_used_idx) {
> > + *len = 0;
> > + return NULL;
> > + }
> > +
> > + last_used = vqs[0].last_used_idx % VHOST_NDESCS;
> > + i = le32toh(used->ring[last_used].id);
> > + *len = le32toh(used->ring[last_used].len);
> > +
> > + /* Make sure the kernel is consuming the descriptors in order */
> > + if (i != last_used) {
> > + die("vhost: id %u at used position %u != %u", i, last_used, i);
> > + return NULL;
> > + }
> > +
> > + if (*len > PKT_BUF_BYTES/VHOST_NDESCS) {
>
> You want your L1_MAX_LEN_VHOST (or whatever it's called) again here.
>
> > + warn("vhost: id %d len %u > %zu", i, *len, PKT_BUF_BYTES/VHOST_NDESCS);
>
> And here.
>
> > + return NULL;
> > + }
> > +
> > + /* TODO check if the id is valid and it has not been double used */
> > + vqs[0].last_used_idx++;
> > + vqs[0].num_free++;
> > + return pkt_buf + i * (PKT_BUF_BYTES/VHOST_NDESCS);
>
> And here.
>
> > +}
> > +
> > +/* TODO this assumes the kernel consumes descriptors in order */
> > +static void rx_pkt_refill(struct ctx *c)
> > +{
> > + /* TODO: tune this threshold */
> > + if (!vqs[0].num_free)
> > + return;
> > +
> > + vring_avail_0.avail.idx += vqs[0].num_free;
> > + vqs[0].num_free = 0;
> > + vhost_kick(&vring_used_0.used, c->vq[0].kick_fd);
> > +}
> > +
> > +void tap_vhost_input(struct ctx *c, union epoll_ref ref, const struct timespec *now)
> > +{
> > + eventfd_read(ref.fd, (eventfd_t[]){ 0 });
> > +
> > + tap_flush_pools();
> > +
> > + while (true) {
> > + struct virtio_net_hdr_mrg_rxbuf *hdr;
> > + unsigned len;
> > +
> > + hdr = virtqueue_get_rx_buf(ref.queue, &len);
> > + if (!hdr)
> > + break;
>
> You could use while ((hdr = virtqueue_get_rx_buf(...))) instead of
> while (true), couldn't you?
>
Sure.
> > +
> > + if (len < sizeof(*hdr)) {
>
> You should check that there are enough bytes for the L2 header as well
> as the mrg_rxbuf header - tap_add_packet() doesn't appear to check
> that itself (maybe it should).
>
That's right, I thought tap_add_packet did it but now I realize it
assumes there is.
Why not check it in tap_add_packet, as all namespace input needs to go
there? We need to adapt the function signature though.
> > + warn("vhost: invalid len %u", len);
> > + continue;
> > + }
> > +
> > + /* TODO this will break from this moment */
> > + if (hdr->num_buffers != 1) {
> > + warn("vhost: Too many buffers %u, %zu bytes should be enough for everybody!", hdr->num_buffers, PKT_BUF_BYTES/VHOST_NDESCS);
>
> L1_MAX_LEN_VHOST again.
I'm ok with moving to L1_MAX_LEN_VHOST, but then another bunch of
places would need L1_MAX_LEN_VHOST*VHOST_NDESCS. Or should we keep the
two macros in parallel?
> Also passt style keeps lines below 80
> columns.
>
Fixing for the next RFC.
> > + continue;
> > + }
> > +
> > + /* TODO fix the v6 pool to support ipv6 */
>
> Not clear on why anything particular is needed for ipv6 here.
>
This comment is outdated actually, I only used pool_tap4 here. Fixing
in the next revision!
> > + tap_add_packet(c, len - sizeof(*hdr), (void *)(hdr+1), now);
> > + }
> > +
> > + tap_handler(c, now);
> > + rx_pkt_refill(c);
> > +}
> > +
> > /**
> > * tap_ns_tun() - Get tuntap fd in namespace
> > * @c: Execution context
> > @@ -1396,10 +1537,14 @@ void tap_listen_handler(struct ctx *c, uint32_t events)
> > */
> > static int tap_ns_tun(void *arg)
> > {
> > + /* TODO we need to check if vhost support VIRTIO_NET_F_MRG_RXBUF and VHOST_NET_F_VIRTIO_NET_HDR actually */
> > + static const uint64_t features =
> > + (1ULL << VIRTIO_F_VERSION_1) | (1ULL << VIRTIO_NET_F_MRG_RXBUF) | (1ULL << VHOST_NET_F_VIRTIO_NET_HDR);
>
> If I understand the code above properly, for the Rx path at least we
> don't need or want F_MRG_RXBUF. So why require it here? If we want
> it for the Tx path, we can omit it here and add it later in the
> series.
>
I can try without it, sure.
> > struct ifreq ifr = { .ifr_flags = IFF_TAP | IFF_NO_PI };
> > int flags = O_RDWR | O_NONBLOCK | O_CLOEXEC;
> > struct ctx *c = (struct ctx *)arg;
> > - int fd, rc;
> > + unsigned i;
> > + int fd, vhost_fd, rc;
> >
> > c->fd_tap = -1;
> > memcpy(ifr.ifr_name, c->pasta_ifn, IFNAMSIZ);
> > @@ -1409,6 +1554,143 @@ static int tap_ns_tun(void *arg)
> > if (fd < 0)
> > die_perror("Failed to open() /dev/net/tun");
> >
> > + vhost_fd = open("/dev/vhost-net", flags);
> > + if (vhost_fd < 0)
> > + die_perror("Failed to open() /dev/vhost-net");
> > +
> > + rc = ioctl(vhost_fd, VHOST_SET_OWNER, NULL);
> > + if (rc < 0)
> > + die_perror("VHOST_SET_OWNER ioctl on /dev/vhost-net failed");
> > +
> > + rc = ioctl(vhost_fd, VHOST_GET_FEATURES, &c->virtio_features);
> > + if (rc < 0)
> > + die_perror("VHOST_GET_FEATURES ioctl on /dev/vhost-net failed");
> > +
> > + /* TODO inform more explicitely */
> > + fprintf(stderr, "vhost features: %lx\n", c->virtio_features);
> > + fprintf(stderr, "req features: %lx\n", features);
>
> You should use info() or debug() here.
>
Right, I'll change.
> > + c->virtio_features &= features;
> > + if (c->virtio_features != features)
> > + die("vhost does not support required features");
> > +
> > + for (i = 0; i < ARRAY_SIZE(c->vq); i++) {
> > + struct vhost_vring_file file = {
> > + .index = i,
> > + };
> > + union epoll_ref ref = { .type = EPOLL_TYPE_VHOST_CALL,
> > + .queue = i };
> > + struct epoll_event ev;
> > +
> > + file.fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
> > + ref.fd = file.fd;
> > + if (file.fd < 0)
> > + die_perror("Failed to create call eventfd");
>
> I know it's technically harmless, but it reads better for me if you
> check for the error immediately after the eventfd() call, before doing
> something else with the value.
>
Changing in the next version.
> > +
> > + rc = ioctl(vhost_fd, VHOST_SET_VRING_CALL, &file);
> > + if (rc < 0)
> > + die_perror(
> > + "VHOST_SET_VRING_CALL ioctl on /dev/vhost-net failed");
> > +
> > + ev = (struct epoll_event){ .data.u64 = ref.u64, .events = EPOLLIN };
> > + if (i == 0) {
> > + rc = epoll_ctl(c->epollfd, EPOLL_CTL_ADD, ref.fd, &ev);
> > + if (rc < 0)
> > + die_perror("Failed to add call eventfd to epoll");
> > + }
> > + c->vq[i].call_fd = file.fd;
> > + }
> > +
> > + /*
> > + * Unlike most of C-style APIs, userspace_addr+memory_size is
> > + * also accesible by the kernel. Include a -1 to adjust.
> > + */
> > +#define VHOST_MEMORY_REGION_PTR(addr, size) \
> > + (struct vhost_memory_region) { \
> > + .guest_phys_addr = (uintptr_t)addr, \
> > + .memory_size = size - 1, \
> > + .userspace_addr = (uintptr_t)addr, \
> > + }
> > +#define VHOST_MEMORY_REGION(elem) VHOST_MEMORY_REGION_PTR(&elem, sizeof(elem))
> > +
> > + /* 1:1 translation */
> > + vhost_memory.mem.regions[0] = VHOST_MEMORY_REGION(vring_desc);
> > + vhost_memory.mem.regions[1] = VHOST_MEMORY_REGION(vring_avail_0);
> > + vhost_memory.mem.regions[2] = VHOST_MEMORY_REGION(vring_avail_1);
> > + vhost_memory.mem.regions[3] = VHOST_MEMORY_REGION(vring_used_0);
> > + vhost_memory.mem.regions[4] = VHOST_MEMORY_REGION(vring_used_1);
> > + vhost_memory.mem.regions[5] = VHOST_MEMORY_REGION(pkt_buf);
> > + static_assert(5 < N_VHOST_REGIONS);
> > +#undef VHOST_MEMORY_REGION
> > +#undef VHOST_MEMORY_REGION_PTR
> > +
> > + rc = ioctl(vhost_fd, VHOST_SET_MEM_TABLE, &vhost_memory.mem);
> > + if (rc < 0)
> > + die_perror(
> > + "VHOST_SET_MEM_TABLE ioctl on /dev/vhost-net failed");
> > +
> > + /* TODO: probably it increases RX perf */
> > +#if 0
> > + struct ifreq ifr;
> > + memset(&ifr, 0, sizeof(ifr));
> > +
> > + if (ioctl(fd, TUNGETIFF, &ifr) != 0)
> > + die_perror("Unable to query TUNGETIFF on FD %d", fd);
> > + }
> > +
> > + if (ifr.ifr_flags & IFF_VNET_HDR)
> > + net->dev.features &= ~(1ULL << VIRTIO_NET_F_MRG_RXBUF);
> > +#endif
> > + rc = ioctl(vhost_fd, VHOST_SET_FEATURES, &c->virtio_features);
> > + if (rc < 0)
> > + die_perror("VHOST_SET_FEATURES ioctl on /dev/vhost-net failed");
> > +
> > + /* Duplicating foreach queue to follow the exact order from QEMU */
> > + for (i = 0; i < ARRAY_SIZE(c->vq); i++) {
> > + struct vhost_vring_addr addr = {
> > + .index = i,
> > + .desc_user_addr = (unsigned long)vring_desc[i],
> > + .avail_user_addr = i == 0 ? (unsigned long)&vring_avail_0 :
> > + (unsigned long)&vring_avail_1,
> > + .used_user_addr = i == 0 ? (unsigned long)&vring_used_0 :
> > + (unsigned long)&vring_used_1,
> > + /* GPA addr */
> > + .log_guest_addr = i == 0 ? (unsigned long)&vring_used_0 :
> > + (unsigned long)&vring_used_1,
> > + };
> > + struct vhost_vring_state state = {
> > + .index = i,
> > + .num = VHOST_NDESCS,
> > + };
> > + struct vhost_vring_file file = {
> > + .index = i,
> > + };
> > +
> > + rc = ioctl(vhost_fd, VHOST_SET_VRING_NUM, &state);
> > + if (rc < 0)
> > + die_perror(
> > + "VHOST_SET_VRING_NUM ioctl on /dev/vhost-net failed");
> > +
> > + fprintf(stderr, "qid: %d\n", i);
> > + fprintf(stderr, "vhost desc addr: 0x%llx\n", addr.desc_user_addr);
> > + fprintf(stderr, "vhost avail addr: 0x%llx\n", addr.avail_user_addr);
> > + fprintf(stderr, "vhost used addr: 0x%llx\n", addr.used_user_addr);
> > + rc = ioctl(vhost_fd, VHOST_SET_VRING_ADDR, &addr);
> > + if (rc < 0)
> > + die_perror(
> > + "VHOST_SET_VRING_ADDR ioctl on /dev/vhost-net failed");
> > +
> > + file.fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
> > + if (file.fd < 0)
> > + die_perror("Failed to create kick eventfd");
> > + rc = ioctl(vhost_fd, VHOST_SET_VRING_KICK, &file);
> > + if (rc < 0)
> > + die_perror(
> > + "VHOST_SET_VRING_KICK ioctl on /dev/vhost-net failed");
> > + c->vq[i].kick_fd = file.fd;
> > +
> > + vqs[i].num_free = VHOST_NDESCS;
> > + }
> > +
> > rc = ioctl(fd, (int)TUNSETIFF, &ifr);
> > if (rc < 0)
> > die_perror("TUNSETIFF ioctl on /dev/net/tun failed");
> > @@ -1416,7 +1698,34 @@ static int tap_ns_tun(void *arg)
> > if (!(c->pasta_ifi = if_nametoindex(c->pasta_ifn)))
> > die("Tap device opened but no network interface found");
> >
> > + for (i = 0; i < VHOST_NDESCS; ++i) {
> > + vring_desc[0][i].addr = (uintptr_t)pkt_buf + i * (PKT_BUF_BYTES/VHOST_NDESCS);
> > + vring_desc[0][i].len = PKT_BUF_BYTES/VHOST_NDESCS;
> > + vring_desc[0][i].flags = VRING_DESC_F_WRITE;
> > + }
> > + for (i = 0; i < VHOST_NDESCS; ++i) {
>
> No { } for single line blocks in passt style.
>
I'll change for the next version.
> > + vring_avail_0.avail.ring[i] = i;
> > + }
> > +
> > + rx_pkt_refill(c);
> > +
> > + /* Duplicating foreach queue to follow the exact order from QEMU */
> > + for (int i = 0; i < ARRAY_SIZE(c->vq); i++) {
> > + struct vhost_vring_file file = {
> > + .index = i,
> > + .fd = fd,
> > + };
> > +
> > + fprintf(stderr, "qid: %d\n", file.index);
> > + fprintf(stderr, "tap fd: %d\n", file.fd);
> > + rc = ioctl(vhost_fd, VHOST_NET_SET_BACKEND, &file);
> > + if (rc < 0)
> > + die_perror(
> > + "VHOST_NET_SET_BACKEND ioctl on /dev/vhost-net failed");
> > + }
> > +
> > c->fd_tap = fd;
> > + c->fd_vhost = vhost_fd;
> >
> > return 0;
> > }
> > diff --git a/tap.h b/tap.h
> > index 6fe3d15..ff8cee5 100644
> > --- a/tap.h
> > +++ b/tap.h
> > @@ -69,6 +69,14 @@ static inline void tap_hdr_update(struct tap_hdr *thdr, size_t l2len)
> > thdr->vnet_len = htonl(l2len);
> > }
> >
> > +/**
> > + * tap_vhost_input() - Handler for new data on the socket to hypervisor vq
> > + * @c: Execution context
> > + * @ref: epoll reference
> > + * @now: Current timestamp
> > + */
>
> passt convention puts these comments in the .c file not the .h, even
> for exported functions.
>
Ouch, I missed that.
> > +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, uint16_t proto);
> > void *tap_push_ip4h(struct iphdr *ip4h, struct in_addr src,
>
[1] https://archives.passt.top/passt-dev/Z-3oQcL28QuIh6LT@zatzit/
next prev parent reply other threads:[~2025-07-28 16:34 UTC|newest]
Thread overview: 42+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-07-09 17:47 [RFC v2 00/11] Add vhost-net kernel support Eugenio Pérez
2025-07-09 17:47 ` [RFC v2 01/11] tap: implement vhost_call_cb Eugenio Pérez
2025-07-23 6:56 ` David Gibson
2025-07-28 16:33 ` Eugenio Perez Martin [this message]
2025-07-29 0:11 ` David Gibson
2025-07-09 17:47 ` [RFC v2 02/11] tap: add die() on vhost error Eugenio Pérez
2025-07-23 6:58 ` David Gibson
2025-07-09 17:47 ` [RFC v2 03/11] tap: replace tx tap hdr with virtio_nethdr_mrg_rxbuf Eugenio Pérez
2025-07-24 0:17 ` David Gibson
2025-07-28 16:37 ` Eugenio Perez Martin
2025-07-09 17:47 ` [RFC v2 04/11] tcp: export memory regions to vhost Eugenio Pérez
2025-07-23 7:06 ` David Gibson
2025-07-28 16:41 ` Eugenio Perez Martin
2025-07-29 0:25 ` David Gibson
2025-07-09 17:47 ` [RFC v2 05/11] virtio: Fill .next in tx queue Eugenio Pérez
2025-07-23 7:07 ` David Gibson
2025-07-28 16:44 ` Eugenio Perez Martin
2025-07-09 17:47 ` [RFC v2 06/11] tap: move static iov_sock to tcp_buf_data_from_sock Eugenio Pérez
2025-07-23 7:09 ` David Gibson
2025-07-28 16:43 ` Eugenio Perez Martin
2025-07-29 0:28 ` David Gibson
2025-07-09 17:47 ` [RFC v2 07/11] tap: support tx through vhost Eugenio Pérez
2025-07-24 0:24 ` David Gibson
2025-07-24 14:30 ` Stefano Brivio
2025-07-25 0:23 ` David Gibson
2025-07-09 17:47 ` [RFC v2 08/11] tap: add tap_free_old_xmit Eugenio Pérez
2025-07-24 0:32 ` David Gibson
2025-07-28 16:45 ` Eugenio Perez Martin
2025-07-09 17:47 ` [RFC v2 09/11] tcp: start conversion to circular buffer Eugenio Pérez
2025-07-24 1:03 ` David Gibson
2025-07-28 16:55 ` Eugenio Perez Martin
2025-07-29 0:30 ` David Gibson
2025-07-09 17:47 ` [RFC v2 10/11] tap: add poll(2) to used_idx Eugenio Pérez
2025-07-24 1:20 ` David Gibson
2025-07-28 17:03 ` Eugenio Perez Martin
2025-07-29 0:32 ` David Gibson
2025-07-29 7:04 ` Eugenio Perez Martin
2025-07-30 0:32 ` David Gibson
2025-07-09 17:47 ` [RFC v2 11/11] tcp_buf: adding TCP tx circular buffer Eugenio Pérez
2025-07-24 1:33 ` David Gibson
2025-07-28 17:04 ` Eugenio Perez Martin
2025-07-10 9:46 ` [RFC v2 00/11] Add vhost-net kernel support Eugenio Perez Martin
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='CAJaqyWc42NLNU5anS=G4Uf52v2e-ZOVix5CFt3Wg=adns32QPw@mail.gmail.com' \
--to=eperezma@redhat.com \
--cc=david@gibson.dropbear.id.au \
--cc=jasowang@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).