From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: passt.top; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: passt.top; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20251104 header.b=PnXqQVM6; dkim-atps=neutral Received: from mail-wr1-x42a.google.com (mail-wr1-x42a.google.com [IPv6:2a00:1450:4864:20::42a]) by passt.top (Postfix) with ESMTPS id 338165A0275 for ; Sun, 02 Aug 2026 15:23:55 +0200 (CEST) Received: by mail-wr1-x42a.google.com with SMTP id ffacd0b85a97d-47fd4531020so1393901f8f.3 for ; Sun, 02 Aug 2026 06:23:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785677035; x=1786281835; darn=passt.top; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=iveiHR3pztGoz+O/x9Nlhjfe7tMWii7zHQcpKDMR0uA=; b=PnXqQVM6Ai05WNgPTtMWLApenzYRzV+cnbi8NDi3Eh6QcgxYYZfUqV3dl3L2HWOTmE d4bUVdbk7RhLnvJRNzugRJLyaahP13hjItMT3vgRCr3CuZgnl5qu0oe33HL4RKcquooY 33csVcuy9dW/0nGpVm28oIbzoBoHe6aDpBgUqknk8m2+HnQHl62fjF550mfRSBlu3VpI ZYgs+Pe5n+G0zJU8EnuyvI20Wx/B8j5Dd62TpcpcLzN2AeWE1VuM98BiyznTPTBbqxer 5RKwfCbLuoJ/n/xH3p4RfH1VMuYjdy3O9CMPQL3slQK0fZhbhhYbM5uUmJ4d965U7+rC 3jkQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785677035; x=1786281835; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=iveiHR3pztGoz+O/x9Nlhjfe7tMWii7zHQcpKDMR0uA=; b=n9TG/xtGpjKzBQmhB7AfXIF2HB38MoYkQGAFZSDn4uTPEVm/SJJS5tgoKoceeFbVy+ 47320vooT9e9QgKc51Lyr3QFY73XCcN7vVslutWvRiVckcqYbplETZjrKK5I0KwmrOUx 4Z3tZqVt0seeRLD/qJUXiReT1tGlp92CFSZJtnBA5GVOR0afFyGOIeZZSXJucIooL1hB tU8tcSO3mZFEt2I83K/5prZoz7iTLMDczCuA9xDHcNbr8ZxnWM2Df8uG7/8icorfQWhD M1L8MPwEPZ+5I+PGrGWjrZAfpEf4Y8vL2EQLoczeKBlWb5i4qP15+daDLiJycdTsMqI8 jbXw== X-Gm-Message-State: AOJu0YxnSuZizQa6CYpjox/8GY4EMAnPiY5VTz5afJxW16aaF06ZuPNj sNPa0T5/ClyU/Ljn511rKwwqkCi3cv0S6SwbUP4dOH1z2UamLXZgHB4ZVTJQdj6g X-Gm-Gg: AR+sD12W2SssRnplMeqCsazhfOX1gu0MQYbl5zdNPd5FMmkhoUbJ+3rtSLCP3tqJAjC AmVNFocWBhwtVYMfOR6SOE82Dl5ubdifWvSEn94dpeTO8Vtf5UZwO1ai2d+g1RxA0BHyZgiyLVN yHc97FXbpfVKczqndL9va9PBAIsOLAHYTNs7V5+zIf6fOU66qFVO1zgKupKPZle1OYJ57LZEjfq jszote+sN2Ovm8sBalT+TQfNUUyG1oiPqGGmniByEgt4Zhu+kdISNztWb23/nzJRjPnm3o/nZgZ 7kEpBYXEf/k9ww1ggqXA5bAvxzH6nXBJZuP7KKmTutMtkQ0raH0NWYmVQxJS6Mrqw7GExreLZ1Y f44u8vApZnc0dCQrq4e8onpoR4wEaiJwkL0LpuRW8hm5OyOXhQiT0DjzdJRZDiX6iRujd5WFatc lYapXxcYq/obIjMmVJhvRAVWZeprlI8CERkSKYC8hO+JbkYCGzIPsBiWJsa3cXnYppeFZCq/ceQ ADgr1d8Yie7Fun+YtrHnR1ACXUNTdrMgcUBZfRXiYYjkTo= X-Received: by 2002:a05:600c:6092:b0:497:ff28:6deb with SMTP id 5b1f17b1804b1-4980c64b8bfmr150866375e9.11.1785677034569; Sun, 02 Aug 2026 06:23:54 -0700 (PDT) Received: from localhost.localdomain ([196.137.15.128]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4980869106csm180745155e9.9.2026.08.02.06.23.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 02 Aug 2026 06:23:54 -0700 (PDT) From: Ammar Yasser To: passt-dev@passt.top Subject: [RFC v3 8/8] tap: Implement pasta vhost TX Date: Sun, 2 Aug 2026 13:21:55 +0000 Message-Id: <20260802132155.870796-9-aerosound161@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260802132155.870796-1-aerosound161@gmail.com> References: <20260802132155.870796-1-aerosound161@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Message-ID-Hash: 34DTZRJ4WNOOIXZ3JB44FX26CIQ3UT3N X-Message-ID-Hash: 34DTZRJ4WNOOIXZ3JB44FX26CIQ3UT3N X-MailFrom: aerosound161@gmail.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: eperezma@redhat.com, Ammar Yasser X-Mailman-Version: 3.3.8 Precedence: list List-Id: Development discussion and patches for passt Archived-At: Archived-At: List-Archive: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: 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 --- 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