From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: passt.top; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: passt.top; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=CyuIGBjv; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by passt.top (Postfix) with ESMTP id 471CC5A004C for ; Fri, 23 Aug 2024 00:14:41 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1724364880; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=NV4fkdfdTpN4zrFoNfzWwl+bWaofPK5YPyb/3I3jE0A=; b=CyuIGBjv+jHXkamDPfKlWYN7VT0uJ/UovTYYKs7OxnXdMntlLcf6a+W+A5ga7FHb20xF3B FXxHceYVRuNbif9HnHe89J/kHpr52Yn6J0xFaU9hOVEnAn1iEb9do69J4rJXYEOeIRHFzS PLQggUIl8E+Bqq1M5yzCj2VPVien/qg= Received: from mail-pj1-f71.google.com (mail-pj1-f71.google.com [209.85.216.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-85-FeU_1sOGNXSCft5HESb-Yg-1; Thu, 22 Aug 2024 18:14:38 -0400 X-MC-Unique: FeU_1sOGNXSCft5HESb-Yg-1 Received: by mail-pj1-f71.google.com with SMTP id 98e67ed59e1d1-2d40f92f0a2so1325574a91.0 for ; Thu, 22 Aug 2024 15:14:38 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724364877; x=1724969677; h=content-transfer-encoding:mime-version:organization:references :in-reply-to:message-id:subject:cc:to:from:date:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=NV4fkdfdTpN4zrFoNfzWwl+bWaofPK5YPyb/3I3jE0A=; b=Zic5syNjTVMYhuk7C17/8BtMJHnmWPP9eogKSL3DDzv8mQxg8PzRW4bQ93+vpfHgdL LVTY7P6uQmCbOX6XcOKa7PqC5B9dHulfmN593KfaSMA6oKs/lVnFVDkkjGjzt9mNcuSs i2Q8tMnSOm6Rqcs6+J89u3FsYSJ3Zr3dvK1UVHv1gdCIG9rIIemygacZKgsBccCxzjSe JtuSeaH8GOdcQwE0Mq1pcQLGd6OwytlHHZxq2VBaPW7S1ESv6fgt8audb3EsunqzpgOa EV0oR/uYvsh7q9dckHbX/agnzueIt9cEQbnDfzQ53doOOFGHPPhw1ctX1Bz6I6agxYH+ jdQw== X-Gm-Message-State: AOJu0Yw1H3t/BswgazKb6rTu7kqge14Ch09Yr+DvC/wVA984spj2WYpI n2bNxYGp2ovPFqZQ32wfi69l1Elj9UgFvHnDBSJjTSOpGHSaVXliA4AUpZRtgtkGznoSv0JTEDL B6m7bpkwcyfUFP6qryq3oAIodUAbmVduGrRmTXFp19vSGUqW6naJtVG9UF4DQaldpF2U9nJRcJx 2VwPjhm+MU0s1/zqH1hKIb7v6+ALhzRs1y X-Received: by 2002:a17:90a:d502:b0:2c9:6f8d:7270 with SMTP id 98e67ed59e1d1-2d646d87891mr96209a91.42.1724364876610; Thu, 22 Aug 2024 15:14:36 -0700 (PDT) X-Google-Smtp-Source: AGHT+IHGn19UA+Iyxjuo6gSX42pCsrAoxgM/ibZ4VsbuoRAwQJG8cdyh24V8iA57+34HBqp6ooe2XQ== X-Received: by 2002:a17:90a:d502:b0:2c9:6f8d:7270 with SMTP id 98e67ed59e1d1-2d646d87891mr96167a91.42.1724364875475; Thu, 22 Aug 2024 15:14:35 -0700 (PDT) Received: from maya.myfinge.rs (ifcgrfdd.trafficplex.cloud. [176.103.220.4]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-2d5eba2199dsm4720570a91.27.2024.08.22.15.14.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 22 Aug 2024 15:14:34 -0700 (PDT) Date: Fri, 23 Aug 2024 00:14:31 +0200 From: Stefano Brivio To: Laurent Vivier Subject: Re: [PATCH v3 4/4] vhost-user: add vhost-user Message-ID: <20240823001431.3414cea1@elisabeth> In-Reply-To: <20240815155024.827956-5-lvivier@redhat.com> References: <20240815155024.827956-1-lvivier@redhat.com> <20240815155024.827956-5-lvivier@redhat.com> Organization: Red Hat X-Mailer: Claws Mail 4.2.0 (GTK 3.24.41; x86_64-pc-linux-gnu) MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit Message-ID-Hash: GR3UASQAR44L7YL55G2TVG6FIK6QBGPK X-Message-ID-Hash: GR3UASQAR44L7YL55G2TVG6FIK6QBGPK X-MailFrom: sbrivio@redhat.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: passt-dev@passt.top 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: On Thu, 15 Aug 2024 17:50:23 +0200 Laurent Vivier wrote: > add virtio and vhost-user functions to connect with QEMU. > > $ ./passt --vhost-user > > and > > # qemu-system-x86_64 ... -m 4G \ > -object memory-backend-memfd,id=memfd0,share=on,size=4G \ > -numa node,memdev=memfd0 \ > -chardev socket,id=chr0,path=/tmp/passt_1.socket \ > -netdev vhost-user,id=netdev0,chardev=chr0 \ > -device virtio-net,mac=9a:2b:2c:2d:2e:2f,netdev=netdev0 \ > ... > > Signed-off-by: Laurent Vivier > --- > Makefile | 6 +- > checksum.c | 1 - > conf.c | 24 +- > epoll_type.h | 4 + > isolation.c | 15 +- > packet.c | 13 ++ > packet.h | 2 + > passt.c | 25 ++- > passt.h | 6 + > pcap.c | 1 - > tap.c | 106 +++++++-- > tap.h | 5 +- > tcp.c | 33 ++- > tcp_buf.c | 6 +- > tcp_internal.h | 3 +- > tcp_vu.c | 593 +++++++++++++++++++++++++++++++++++++++++++++++++ > tcp_vu.h | 12 + > udp.c | 71 +++--- > udp.h | 8 +- > udp_internal.h | 34 +++ > udp_vu.c | 338 ++++++++++++++++++++++++++++ > udp_vu.h | 13 ++ > vhost_user.c | 28 ++- > virtio.c | 1 - > vu_common.c | 27 +++ > vu_common.h | 34 +++ > 26 files changed, 1310 insertions(+), 99 deletions(-) > create mode 100644 tcp_vu.c > create mode 100644 tcp_vu.h > create mode 100644 udp_internal.h > create mode 100644 udp_vu.c > create mode 100644 udp_vu.h > create mode 100644 vu_common.c > create mode 100644 vu_common.h > > diff --git a/Makefile b/Makefile > index 4ccefffacfde..4fb178932f8e 100644 > --- a/Makefile > +++ b/Makefile > @@ -47,7 +47,8 @@ FLAGS += -DDUAL_STACK_SOCKETS=$(DUAL_STACK_SOCKETS) > PASST_SRCS = arch.c arp.c checksum.c conf.c dhcp.c dhcpv6.c flow.c fwd.c \ > icmp.c igmp.c inany.c iov.c ip.c isolation.c lineread.c log.c mld.c \ > ndp.c netlink.c packet.c passt.c pasta.c pcap.c pif.c tap.c tcp.c \ > - tcp_buf.c tcp_splice.c udp.c udp_flow.c util.c vhost_user.c virtio.c > + tcp_buf.c tcp_splice.c tcp_vu.c udp.c udp_flow.c udp_vu.c util.c \ > + vhost_user.c virtio.c vu_common.c > QRAP_SRCS = qrap.c > SRCS = $(PASST_SRCS) $(QRAP_SRCS) > > @@ -57,7 +58,8 @@ PASST_HEADERS = arch.h arp.h checksum.h conf.h dhcp.h dhcpv6.h flow.h fwd.h \ > flow_table.h icmp.h icmp_flow.h inany.h iov.h ip.h isolation.h \ > lineread.h log.h ndp.h netlink.h packet.h passt.h pasta.h pcap.h pif.h \ > siphash.h tap.h tcp.h tcp_buf.h tcp_conn.h tcp_internal.h tcp_splice.h \ > - udp.h udp_flow.h util.h vhost_user.h virtio.h > + tcp_vu.h udp.h udp_flow.h udp_internal.h udp_vu.h util.h vhost_user.h \ > + virtio.h vu_common.h > HEADERS = $(PASST_HEADERS) seccomp.h > > C := \#include \nstruct tcp_info x = { .tcpi_snd_wnd = 0 }; > diff --git a/checksum.c b/checksum.c > index 006614fcbb28..aa5b7ae1cb66 100644 > --- a/checksum.c > +++ b/checksum.c > @@ -501,7 +501,6 @@ uint16_t csum(const void *buf, size_t len, uint32_t init) > * > * Return: 16-bit folded, complemented checksum > */ > -/* cppcheck-suppress unusedFunction */ > uint16_t csum_iov(const struct iovec *iov, size_t n, uint32_t init) > { > unsigned int i; > diff --git a/conf.c b/conf.c > index 46fcd9126b4c..c684dbaac694 100644 > --- a/conf.c > +++ b/conf.c > @@ -45,6 +45,7 @@ > #include "lineread.h" > #include "isolation.h" > #include "log.h" > +#include "vhost_user.h" > > /** > * next_chunk - Return the next piece of a string delimited by a character > @@ -759,9 +760,14 @@ static void usage(const char *name, FILE *f, int status) > " default: same interface name as external one\n"); > } else { > fprintf(f, > - " -s, --socket PATH UNIX domain socket path\n" > + " -s, --socket, --socket-path PATH UNIX domain socket path\n" > " default: probe free path starting from " > UNIX_SOCK_PATH "\n", 1); > + fprintf(f, > + " --vhost-user Enable vhost-user mode\n" > + " UNIX domain socket is provided by -s option\n" > + " --print-capabilities print back-end capabilities in JSON format,\n" > + " only meaningful for vhost-user mode\n"); I actually wonder if we should even advertise here --socket-path and --print-capabilities. I mean, the specification requires us to support them, not to document them, right? In any case, they (at least --vhost-user) should also be mentioned in the man page. > } > > fprintf(f, > @@ -1230,6 +1236,10 @@ void conf(struct ctx *c, int argc, char **argv) > {"no-copy-routes", no_argument, NULL, 18 }, > {"no-copy-addrs", no_argument, NULL, 19 }, > {"netns-only", no_argument, NULL, 20 }, > + {"vhost-user", no_argument, NULL, 21 }, > + /* vhost-user backend program convention */ > + {"print-capabilities", no_argument, NULL, 22 }, > + {"socket-path", required_argument, NULL, 's' }, > { 0 }, > }; > const char *logname = (c->mode == MODE_PASTA) ? "pasta" : "passt"; > @@ -1359,14 +1369,12 @@ void conf(struct ctx *c, int argc, char **argv) > sizeof(c->ip4.ifname_out), "%s", optarg); > if (ret <= 0 || ret >= (int)sizeof(c->ip4.ifname_out)) > die("Invalid interface name: %s", optarg); > - Unrelated change. > break; > case 16: > ret = snprintf(c->ip6.ifname_out, > sizeof(c->ip6.ifname_out), "%s", optarg); > if (ret <= 0 || ret >= (int)sizeof(c->ip6.ifname_out)) > die("Invalid interface name: %s", optarg); > - Unrelated change. > break; > case 17: > if (c->mode != MODE_PASTA) > @@ -1395,6 +1403,16 @@ void conf(struct ctx *c, int argc, char **argv) > netns_only = 1; > *userns = 0; > break; > + case 21: > + if (c->mode == MODE_PASTA) { > + err("--vhost-user is for passt mode only"); > + usage(argv[0], stdout, EXIT_SUCCESS); > + } > + c->mode = MODE_VU; > + break; > + case 22: > + vu_print_capabilities(); > + break; > case 'd': > c->debug = 1; > c->quiet = 0; > diff --git a/epoll_type.h b/epoll_type.h > index 0ad1efa0ccec..f3ef41584757 100644 > --- a/epoll_type.h > +++ b/epoll_type.h > @@ -36,6 +36,10 @@ enum epoll_type { > EPOLL_TYPE_TAP_PASST, > /* socket listening for qemu socket connections */ > EPOLL_TYPE_TAP_LISTEN, > + /* vhost-user command socket */ > + EPOLL_TYPE_VHOST_CMD, > + /* vhost-user kick event socket */ > + EPOLL_TYPE_VHOST_KICK, > > EPOLL_NUM_TYPES, > }; > diff --git a/isolation.c b/isolation.c > index 4956d7e6f331..1a27f066c2ba 100644 > --- a/isolation.c > +++ b/isolation.c > @@ -373,12 +373,19 @@ void isolate_postfork(const struct ctx *c) > > prctl(PR_SET_DUMPABLE, 0); > > - if (c->mode == MODE_PASTA) { > - prog.len = (unsigned short)ARRAY_SIZE(filter_pasta); > - prog.filter = filter_pasta; > - } else { > + switch (c->mode) { > + case MODE_PASST: > prog.len = (unsigned short)ARRAY_SIZE(filter_passt); > prog.filter = filter_passt; > + break; > + case MODE_PASTA: > + prog.len = (unsigned short)ARRAY_SIZE(filter_pasta); > + prog.filter = filter_pasta; > + break; > + case MODE_VU: > + prog.len = (unsigned short)ARRAY_SIZE(filter_vu); > + prog.filter = filter_vu; > + break; > } > > if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) || > diff --git a/packet.c b/packet.c > index 37489961a37e..36c7e5070831 100644 > --- a/packet.c > +++ b/packet.c > @@ -36,6 +36,19 @@ > static int packet_check_range(const struct pool *p, size_t offset, size_t len, > const char *start, const char *func, int line) > { > + ASSERT(p->buf); I would rather keep this path ASSERT-free, because it's meant as a safety net. > + > + if (p->buf_size == 0) { It would be convenient to have this special value (if I understood correctly) of buf_size documented for struct pool, @buf_size: Total size of buffer, 0 for passt vhost-user mode And I guess we shouldn't call vu_packet_check_range() by accident if MODE_VU isn't set. > + int ret; > + > + ret = vu_packet_check_range((void *)p->buf, offset, len, start); > + > + if (ret == -1) > + trace("cannot find region, %s:%i", func, line); > + > + return ret; > + } > + > if (start < p->buf) { > trace("packet start %p before buffer start %p, " > "%s:%i", (void *)start, (void *)p->buf, func, line); > diff --git a/packet.h b/packet.h > index 8377dcf678bb..d32688d8a0a4 100644 > --- a/packet.h > +++ b/packet.h > @@ -22,6 +22,8 @@ struct pool { > struct iovec pkt[1]; > }; > > +int vu_packet_check_range(void *buf, size_t offset, size_t len, > + const char *start); > void packet_add_do(struct pool *p, size_t len, const char *start, > const char *func, int line); > void *packet_get_do(const struct pool *p, const size_t idx, > diff --git a/passt.c b/passt.c > index 6401730dae65..a931a55ab31b 100644 > --- a/passt.c > +++ b/passt.c > @@ -74,6 +74,8 @@ char *epoll_type_str[] = { > [EPOLL_TYPE_TAP_PASTA] = "/dev/net/tun device", > [EPOLL_TYPE_TAP_PASST] = "connected qemu socket", > [EPOLL_TYPE_TAP_LISTEN] = "listening qemu socket", > + [EPOLL_TYPE_VHOST_CMD] = "vhost-user command socket", > + [EPOLL_TYPE_VHOST_KICK] = "vhost-user kick socket", > }; > static_assert(ARRAY_SIZE(epoll_type_str) == EPOLL_NUM_TYPES, > "epoll_type_str[] doesn't match enum epoll_type"); > @@ -206,6 +208,7 @@ int main(int argc, char **argv) > struct rlimit limit; > struct timespec now; > struct sigaction sa; > + struct vu_dev vdev; > > clock_gettime(CLOCK_MONOTONIC, &log_start); > > @@ -262,6 +265,8 @@ int main(int argc, char **argv) > pasta_netns_quit_init(&c); > > tap_sock_init(&c); > + if (c.mode == MODE_VU) > + vu_init(&c, &vdev); > > secret_init(&c); > > @@ -350,14 +355,30 @@ loop: > tcp_timer_handler(&c, ref); > break; > case EPOLL_TYPE_UDP_LISTEN: > - udp_listen_sock_handler(&c, ref, eventmask, &now); > + if (c.mode == MODE_VU) Curly brackets for multi-line statements. > + udp_vu_listen_sock_handler(&c, ref, eventmask, > + &now); > + else > + udp_buf_listen_sock_handler(&c, ref, eventmask, > + &now); > break; > case EPOLL_TYPE_UDP_REPLY: > - udp_reply_sock_handler(&c, ref, eventmask, &now); > + if (c.mode == MODE_VU) > + udp_vu_reply_sock_handler(&c, ref, eventmask, > + &now); > + else > + udp_buf_reply_sock_handler(&c, ref, eventmask, > + &now); > break; > case EPOLL_TYPE_PING: > icmp_sock_handler(&c, ref); > break; > + case EPOLL_TYPE_VHOST_CMD: > + tap_handler_vu(&vdev, c.fd_tap, eventmask); > + break; > + case EPOLL_TYPE_VHOST_KICK: > + vu_kick_cb(&vdev, ref, &now); > + break; > default: > /* Can't happen */ > ASSERT(0); > diff --git a/passt.h b/passt.h > index d0f31a230976..71ad32aa3dd0 100644 > --- a/passt.h > +++ b/passt.h > @@ -25,6 +25,8 @@ union epoll_ref; > #include "fwd.h" > #include "tcp.h" > #include "udp.h" > +#include "udp_vu.h" > +#include "vhost_user.h" > > /** > * union epoll_ref - Breakdown of reference for epoll fd bookkeeping > @@ -87,6 +89,7 @@ struct fqdn { > enum passt_modes { > MODE_PASST, > MODE_PASTA, > + MODE_VU, > }; > > /** > @@ -193,6 +196,7 @@ struct ip6_ctx { > * @no_map_gw: Don't map connections, untracked UDP to gateway to host > * @low_wmem: Low probed net.core.wmem_max > * @low_rmem: Low probed net.core.rmem_max > + * @vdev: vhost-user device > */ > struct ctx { > enum passt_modes mode; > @@ -256,6 +260,8 @@ struct ctx { > > int low_wmem; > int low_rmem; > + > + struct vu_dev *vdev; > }; > > void proto_update_l2_buf(const unsigned char *eth_d, > diff --git a/pcap.c b/pcap.c > index 46cc4b0d72b6..7e9c56090041 100644 > --- a/pcap.c > +++ b/pcap.c > @@ -140,7 +140,6 @@ void pcap_multiple(const struct iovec *iov, size_t frame_parts, unsigned int n, > * containing packet data to write, including L2 header > * @iovcnt: Number of buffers (@iov entries) > */ > -/* cppcheck-suppress unusedFunction */ > void pcap_iov(const struct iovec *iov, size_t iovcnt) > { > struct timespec now; > diff --git a/tap.c b/tap.c > index 5852705b897c..a25e4e494287 100644 > --- a/tap.c > +++ b/tap.c > @@ -58,6 +58,7 @@ > #include "packet.h" > #include "tap.h" > #include "log.h" > +#include "vhost_user.h" > > /* IPv4 (plus ARP) and IPv6 message batches from tap/guest to IP handlers */ > static PACKET_POOL_NOINIT(pool_tap4, TAP_MSGS, pkt_buf); > @@ -78,16 +79,22 @@ void tap_send_single(const struct ctx *c, const void *data, size_t l2len) > struct iovec iov[2]; > size_t iovcnt = 0; > > - if (c->mode == MODE_PASST) { > + switch (c->mode) { > + case MODE_PASST: > iov[iovcnt] = IOV_OF_LVALUE(vnet_len); > iovcnt++; > - } > - > - iov[iovcnt].iov_base = (void *)data; > - iov[iovcnt].iov_len = l2len; > - 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); > + tap_send_frames(c, iov, iovcnt, 1); > + break; > + case MODE_VU: > + vu_send(c->vdev, data, l2len); > + break; > + } > } > > /** > @@ -406,10 +413,18 @@ size_t tap_send_frames(const struct ctx *c, const struct iovec *iov, > if (!nframes) > return 0; > > - if (c->mode == MODE_PASTA) > + switch (c->mode) { > + case MODE_PASTA: > m = tap_send_frames_pasta(c, iov, bufs_per_frame, nframes); > - else > + break; > + case MODE_PASST: > m = tap_send_frames_passt(c, iov, bufs_per_frame, nframes); > + break; > + case MODE_VU: > + /* fall through */ > + default: > + ASSERT(0); > + } > > if (m < nframes) > debug("tap: failed to send %zu frames of %zu", > @@ -967,7 +982,7 @@ void tap_add_packet(struct ctx *c, ssize_t l2len, char *p) > * tap_sock_reset() - Handle closing or failure of connect AF_UNIX socket > * @c: Execution context > */ > -static void tap_sock_reset(struct ctx *c) > +void tap_sock_reset(struct ctx *c) > { > info("Client connection closed%s", c->one_off ? ", exiting" : ""); > > @@ -978,6 +993,8 @@ static void tap_sock_reset(struct ctx *c) > epoll_ctl(c->epollfd, EPOLL_CTL_DEL, c->fd_tap, NULL); > close(c->fd_tap); > c->fd_tap = -1; > + if (c->mode == MODE_VU) > + vu_cleanup(c->vdev); > } > > /** > @@ -1177,11 +1194,17 @@ static void tap_sock_unix_init(struct ctx *c) > ev.data.u64 = ref.u64; > epoll_ctl(c->epollfd, EPOLL_CTL_ADD, c->fd_tap_listen, &ev); > > - info("\nYou can now start qemu (>= 7.2, with commit 13c6be96618c):"); > - info(" kvm ... -device virtio-net-pci,netdev=s -netdev stream,id=s,server=off,addr.type=unix,addr.path=%s", > - c->sock_path); > - info("or qrap, for earlier qemu versions:"); > - info(" ./qrap 5 kvm ... -net socket,fd=5 -net nic,model=virtio"); > + if (c->mode == MODE_VU) { > + info("You can start qemu with:"); > + info(" kvm ... -chardev socket,id=chr0,path=%s -netdev vhost-user,id=netdev0,chardev=chr0 -device virtio-net,netdev=netdev0 -object memory-backend-memfd,id=memfd0,share=on,size=$RAMSIZE -numa node,memdev=memfd0\n", > + c->sock_path); > + } else { > + info("\nYou can now start qemu (>= 7.2, with commit 13c6be96618c):"); > + info(" kvm ... -device virtio-net-pci,netdev=s -netdev stream,id=s,server=off,addr.type=unix,addr.path=%s", > + c->sock_path); > + info("or qrap, for earlier qemu versions:"); > + info(" ./qrap 5 kvm ... -net socket,fd=5 -net nic,model=virtio"); > + } > } > > /** > @@ -1191,8 +1214,8 @@ static void tap_sock_unix_init(struct ctx *c) > */ > void tap_listen_handler(struct ctx *c, uint32_t events) > { > - union epoll_ref ref = { .type = EPOLL_TYPE_TAP_PASST }; > struct epoll_event ev = { 0 }; > + union epoll_ref ref; > int v = INT_MAX / 2; > struct ucred ucred; > socklen_t len; > @@ -1232,6 +1255,10 @@ void tap_listen_handler(struct ctx *c, uint32_t events) > trace("tap: failed to set SO_SNDBUF to %i", v); > > ref.fd = c->fd_tap; > + if (c->mode == MODE_VU) > + ref.type = EPOLL_TYPE_VHOST_CMD; > + else > + ref.type = EPOLL_TYPE_TAP_PASST; > ev.events = EPOLLIN | EPOLLRDHUP; > ev.data.u64 = ref.u64; > epoll_ctl(c->epollfd, EPOLL_CTL_ADD, c->fd_tap, &ev); > @@ -1293,21 +1320,47 @@ static void tap_sock_tun_init(struct ctx *c) > epoll_ctl(c->epollfd, EPOLL_CTL_ADD, c->fd_tap, &ev); > } > > +void tap_sock_update_buf(void *base, size_t size) Function comment missing. > +{ > + int i; > + > + pool_tap4_storage.buf = base; > + pool_tap4_storage.buf_size = size; > + pool_tap6_storage.buf = base; > + pool_tap6_storage.buf_size = size; > + > + for (i = 0; i < TAP_SEQS; i++) { > + tap4_l4[i].p.buf = base; > + tap4_l4[i].p.buf_size = size; > + tap6_l4[i].p.buf = base; > + tap6_l4[i].p.buf_size = size; > + } > +} > + > /** > * tap_sock_init() - Create and set up AF_UNIX socket or tuntap file descriptor > * @c: Execution context > */ > void tap_sock_init(struct ctx *c) > { > - size_t sz = sizeof(pkt_buf); > + size_t sz; > + char *buf; > int i; > > - pool_tap4_storage = PACKET_INIT(pool_tap4, TAP_MSGS, pkt_buf, sz); > - pool_tap6_storage = PACKET_INIT(pool_tap6, TAP_MSGS, pkt_buf, sz); > + if (c->mode == MODE_VU) { > + buf = NULL; > + sz = 0; > + } else { > + buf = pkt_buf; > + sz = sizeof(pkt_buf); > + } > + > + pool_tap4_storage = PACKET_INIT(pool_tap4, TAP_MSGS, buf, sz); > + pool_tap6_storage = PACKET_INIT(pool_tap6, TAP_MSGS, buf, sz); > > for (i = 0; i < TAP_SEQS; i++) { > - tap4_l4[i].p = PACKET_INIT(pool_l4, UIO_MAXIOV, pkt_buf, sz); > - tap6_l4[i].p = PACKET_INIT(pool_l4, UIO_MAXIOV, pkt_buf, sz); > + tap4_l4[i].p = PACKET_INIT(pool_l4, UIO_MAXIOV, buf, sz); > + tap6_l4[i].p = PACKET_INIT(pool_l4, UIO_MAXIOV, buf, sz); > } > > if (c->fd_tap != -1) { /* Passed as --fd */ > @@ -1316,10 +1369,17 @@ void tap_sock_init(struct ctx *c) > > ASSERT(c->one_off); > ref.fd = c->fd_tap; > - if (c->mode == MODE_PASST) > + switch (c->mode) { > + case MODE_PASST: > ref.type = EPOLL_TYPE_TAP_PASST; > - else > + break; > + case MODE_PASTA: > ref.type = EPOLL_TYPE_TAP_PASTA; > + break; > + case MODE_VU: > + ref.type = EPOLL_TYPE_VHOST_CMD; > + break; > + } > > ev.events = EPOLLIN | EPOLLRDHUP; > ev.data.u64 = ref.u64; > diff --git a/tap.h b/tap.h > index ec9e2acec460..c5447f7077eb 100644 > --- a/tap.h > +++ b/tap.h > @@ -40,7 +40,8 @@ static inline struct iovec tap_hdr_iov(const struct ctx *c, > */ > static inline void tap_hdr_update(struct tap_hdr *thdr, size_t l2len) > { > - thdr->vnet_len = htonl(l2len); > + if (thdr) > + thdr->vnet_len = htonl(l2len); > } > > void tap_udp4_send(const struct ctx *c, struct in_addr src, in_port_t sport, > @@ -68,6 +69,8 @@ void tap_handler_pasta(struct ctx *c, uint32_t events, > void tap_handler_passt(struct ctx *c, uint32_t events, > const struct timespec *now); > int tap_sock_unix_open(char *sock_path); > +void tap_sock_reset(struct ctx *c); > +void tap_sock_update_buf(void *base, size_t size); > void tap_sock_init(struct ctx *c); > void tap_flush_pools(void); > void tap_handler(struct ctx *c, const struct timespec *now); > diff --git a/tcp.c b/tcp.c > index c0820ce7a391..1af99b2ae042 100644 > --- a/tcp.c > +++ b/tcp.c > @@ -304,6 +304,7 @@ > #include "flow_table.h" > #include "tcp_internal.h" > #include "tcp_buf.h" > +#include "tcp_vu.h" > > /* MSS rounding: see SET_MSS() */ > #define MSS_DEFAULT 536 > @@ -896,6 +897,7 @@ static void tcp_fill_header(struct tcphdr *th, > > /** > * tcp_fill_headers4() - Fill 802.3, IPv4, TCP headers in pre-cooked buffers > + * @c: Execution context > * @conn: Connection pointer > * @taph: tap backend specific header > * @iph: Pointer to IPv4 header > @@ -906,7 +908,8 @@ static void tcp_fill_header(struct tcphdr *th, > * > * Return: The IPv4 payload length, host order > */ > -static size_t tcp_fill_headers4(const struct tcp_tap_conn *conn, > +static size_t tcp_fill_headers4(const struct ctx *c, > + const struct tcp_tap_conn *conn, > struct tap_hdr *taph, > struct iphdr *iph, struct tcphdr *th, > size_t dlen, const uint16_t *check, > @@ -929,7 +932,10 @@ static size_t tcp_fill_headers4(const struct tcp_tap_conn *conn, > > tcp_fill_header(th, conn, seq); > > - tcp_update_check_tcp4(iph, th); > + if (c->mode != MODE_VU) Instead of passing 'c', we could pass, all the way down, a "no_tcp_csum" flag, which is set when you call tcp_l2_buf_fill_headers() from tcp_vu.c. Eventually, we'll want to be able to skip the checksum for pasta as well, if I recall correctly. > + tcp_update_check_tcp4(iph, th); > + else > + th->check = 0; > > tap_hdr_update(taph, l3len + sizeof(struct ethhdr)); > > @@ -938,6 +944,7 @@ static size_t tcp_fill_headers4(const struct tcp_tap_conn *conn, > > /** > * tcp_fill_headers6() - Fill 802.3, IPv6, TCP headers in pre-cooked buffers > + * @c: Execution context > * @conn: Connection pointer > * @taph: tap backend specific header > * @ip6h: Pointer to IPv6 header > @@ -948,7 +955,8 @@ static size_t tcp_fill_headers4(const struct tcp_tap_conn *conn, > * > * Return: The IPv6 payload length, host order > */ > -static size_t tcp_fill_headers6(const struct tcp_tap_conn *conn, > +static size_t tcp_fill_headers6(const struct ctx *c, > + const struct tcp_tap_conn *conn, > struct tap_hdr *taph, > struct ipv6hdr *ip6h, struct tcphdr *th, > size_t dlen, uint32_t seq) > @@ -970,7 +978,10 @@ static size_t tcp_fill_headers6(const struct tcp_tap_conn *conn, > > tcp_fill_header(th, conn, seq); > > - tcp_update_check_tcp6(ip6h, th); > + if (c->mode != MODE_VU) > + tcp_update_check_tcp6(ip6h, th); > + else > + th->check = 0; > > tap_hdr_update(taph, l4len + sizeof(*ip6h) + sizeof(struct ethhdr)); > > @@ -979,6 +990,7 @@ static size_t tcp_fill_headers6(const struct tcp_tap_conn *conn, > > /** > * tcp_l2_buf_fill_headers() - Fill 802.3, IP, TCP headers in pre-cooked buffers > + * @c: Execution context > * @conn: Connection pointer > * @iov: Pointer to an array of iovec of TCP pre-cooked buffers > * @dlen: TCP payload length > @@ -987,7 +999,8 @@ static size_t tcp_fill_headers6(const struct tcp_tap_conn *conn, > * > * Return: IP payload length, host order > */ > -size_t tcp_l2_buf_fill_headers(const struct tcp_tap_conn *conn, > +size_t tcp_l2_buf_fill_headers(const struct ctx *c, > + const struct tcp_tap_conn *conn, > struct iovec *iov, size_t dlen, > const uint16_t *check, uint32_t seq) > { > @@ -995,13 +1008,13 @@ size_t tcp_l2_buf_fill_headers(const struct tcp_tap_conn *conn, > const struct in_addr *a4 = inany_v4(&tapside->faddr); > > if (a4) { > - return tcp_fill_headers4(conn, iov[TCP_IOV_TAP].iov_base, > + return tcp_fill_headers4(c, conn, iov[TCP_IOV_TAP].iov_base, > iov[TCP_IOV_IP].iov_base, > iov[TCP_IOV_PAYLOAD].iov_base, dlen, > check, seq); > } > > - return tcp_fill_headers6(conn, iov[TCP_IOV_TAP].iov_base, > + return tcp_fill_headers6(c, conn, iov[TCP_IOV_TAP].iov_base, > iov[TCP_IOV_IP].iov_base, > iov[TCP_IOV_PAYLOAD].iov_base, dlen, > seq); > @@ -1237,6 +1250,9 @@ int tcp_prepare_flags(struct ctx *c, struct tcp_tap_conn *conn, > */ > int tcp_send_flag(struct ctx *c, struct tcp_tap_conn *conn, int flags) > { > + if (c->mode == MODE_VU) > + return tcp_vu_send_flag(c, conn, flags); > + > return tcp_buf_send_flag(c, conn, flags); > } > > @@ -1630,6 +1646,9 @@ static int tcp_sock_consume(const struct tcp_tap_conn *conn, uint32_t ack_seq) > */ > static int tcp_data_from_sock(struct ctx *c, struct tcp_tap_conn *conn) > { > + if (c->mode == MODE_VU) > + return tcp_vu_data_from_sock(c, conn); > + > return tcp_buf_data_from_sock(c, conn); > } > > diff --git a/tcp_buf.c b/tcp_buf.c > index c31e9f31b438..6b702b00be89 100644 > --- a/tcp_buf.c > +++ b/tcp_buf.c > @@ -321,7 +321,7 @@ int tcp_buf_send_flag(struct ctx *c, struct tcp_tap_conn *conn, int flags) > return ret; > } > > - l4len = tcp_l2_buf_fill_headers(conn, iov, optlen, NULL, seq); > + l4len = tcp_l2_buf_fill_headers(c, conn, iov, optlen, NULL, seq); > iov[TCP_IOV_PAYLOAD].iov_len = l4len; > > if (flags & DUP_ACK) { > @@ -378,7 +378,7 @@ static void tcp_data_to_tap(struct ctx *c, struct tcp_tap_conn *conn, > tcp4_frame_conns[tcp4_payload_used] = conn; > > iov = tcp4_l2_iov[tcp4_payload_used++]; > - l4len = tcp_l2_buf_fill_headers(conn, iov, dlen, check, seq); > + l4len = tcp_l2_buf_fill_headers(c, conn, iov, dlen, check, seq); > iov[TCP_IOV_PAYLOAD].iov_len = l4len; > if (tcp4_payload_used > TCP_FRAMES_MEM - 1) > tcp_payload_flush(c); > @@ -386,7 +386,7 @@ static void tcp_data_to_tap(struct ctx *c, struct tcp_tap_conn *conn, > tcp6_frame_conns[tcp6_payload_used] = conn; > > iov = tcp6_l2_iov[tcp6_payload_used++]; > - l4len = tcp_l2_buf_fill_headers(conn, iov, dlen, NULL, seq); > + l4len = tcp_l2_buf_fill_headers(c, conn, iov, dlen, NULL, seq); > iov[TCP_IOV_PAYLOAD].iov_len = l4len; > if (tcp6_payload_used > TCP_FRAMES_MEM - 1) > tcp_payload_flush(c); > diff --git a/tcp_internal.h b/tcp_internal.h > index 8b60aabc1b33..3dd4b49a4441 100644 > --- a/tcp_internal.h > +++ b/tcp_internal.h > @@ -89,7 +89,8 @@ void tcp_rst_do(struct ctx *c, struct tcp_tap_conn *conn); > tcp_rst_do(c, conn); \ > } while (0) > > -size_t tcp_l2_buf_fill_headers(const struct tcp_tap_conn *conn, > +size_t tcp_l2_buf_fill_headers(const struct ctx *c, > + const struct tcp_tap_conn *conn, > struct iovec *iov, size_t dlen, > const uint16_t *check, uint32_t seq); > int tcp_update_seqack_wnd(const struct ctx *c, struct tcp_tap_conn *conn, > diff --git a/tcp_vu.c b/tcp_vu.c > new file mode 100644 > index 000000000000..6eef9187dbd7 > --- /dev/null > +++ b/tcp_vu.c > @@ -0,0 +1,593 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later Same here with the SPDX tag. > + * Copyright Red Hat > + * Author: Laurent Vivier > + * > + * tcp_vu.c - TCP L2 vhost-user management functions > + */ > + > +#include > +#include > +#include > + > +#include > + > +#include > + > +#include > +#include > + > +#include "util.h" > +#include "ip.h" > +#include "passt.h" > +#include "siphash.h" > +#include "inany.h" > +#include "vhost_user.h" > +#include "tcp.h" > +#include "pcap.h" > +#include "flow.h" > +#include "tcp_conn.h" > +#include "flow_table.h" > +#include "tcp_vu.h" > +#include "tcp_internal.h" > +#include "checksum.h" > +#include "vu_common.h" > + > +/** > + * struct tcp_payload_t - TCP header and data to send segments with payload > + * @th: TCP header > + * @data: TCP data > + */ > +struct tcp_payload_t { > + struct tcphdr th; > + uint8_t data[IP_MAX_MTU - sizeof(struct tcphdr)]; > +}; > + > +/** > + * struct tcp_flags_t - TCP header and data to send zero-length > + * segments (flags) > + * @th: TCP header > + * @opts TCP options > + */ > +struct tcp_flags_t { > + struct tcphdr th; > + char opts[OPT_MSS_LEN + OPT_WS_LEN + 1]; > +}; > + > +/* vhost-user */ > +static const struct virtio_net_hdr vu_header = { > + .flags = VIRTIO_NET_HDR_F_DATA_VALID, > + .gso_type = VIRTIO_NET_HDR_GSO_NONE, > +}; > + > +static struct iovec iov_vu[VIRTQUEUE_MAX_SIZE]; > +static struct vu_virtq_element elem[VIRTQUEUE_MAX_SIZE]; > + > +static size_t tcp_vu_l2_hdrlen(const struct vu_dev *vdev, bool v6) Function comment missing. > +{ > + size_t l2_hdrlen; > + > + l2_hdrlen = vdev->hdrlen + sizeof(struct ethhdr) + > + sizeof(struct tcphdr); > + > + if (v6) > + l2_hdrlen += sizeof(struct ipv6hdr); > + else > + l2_hdrlen += sizeof(struct iphdr); > + > + return l2_hdrlen; > +} > + > +static void tcp_vu_pcap(const struct ctx *c, const struct flowside *tapside, > + struct iovec *iov, int iov_used, size_t l4len) Function comment missing. > +{ > + const struct in_addr *src = inany_v4(&tapside->faddr); > + const struct in_addr *dst = inany_v4(&tapside->eaddr); > + const struct vu_dev *vdev = c->vdev; > + char *base = iov[0].iov_base; > + size_t size = iov[0].iov_len; > + struct tcp_payload_t *bp; > + uint32_t sum; > + > + if (!*c->pcap) > + return; > + > + if (src && dst) { > + bp = vu_payloadv4(vdev, base); > + sum = proto_ipv4_header_psum(l4len, IPPROTO_TCP, > + *src, *dst); > + } else { > + bp = vu_payloadv6(vdev, base); > + sum = proto_ipv6_header_psum(l4len, IPPROTO_TCP, > + &tapside->faddr.a6, > + &tapside->eaddr.a6); > + } > + iov[0].iov_base = &bp->th; > + iov[0].iov_len = size - ((char *)iov[0].iov_base - base); > + bp->th.check = 0; > + bp->th.check = csum_iov(iov, iov_used, sum); > + > + /* set iov for pcap logging */ > + iov[0].iov_base = base + vdev->hdrlen; > + iov[0].iov_len = size - vdev->hdrlen; > + > + pcap_iov(iov, iov_used); > + > + /* restore iov[0] */ > + iov[0].iov_base = base; > + iov[0].iov_len = size; > +} > + > +int tcp_vu_send_flag(struct ctx *c, struct tcp_tap_conn *conn, int flags) Function comment missing. > +{ > + struct vu_dev *vdev = c->vdev; > + struct vu_virtq *vq = &vdev->vq[VHOST_USER_RX_QUEUE]; > + const struct flowside *tapside = TAPFLOW(conn); > + struct virtio_net_hdr_mrg_rxbuf *vh; > + struct iovec l2_iov[TCP_NUM_IOVS]; > + size_t l2len, l4len, optlen; > + struct iovec in_sg; > + struct ethhdr *eh; > + int nb_ack; > + int ret; > + > + elem[0].out_num = 0; > + elem[0].out_sg = NULL; > + elem[0].in_num = 1; > + elem[0].in_sg = &in_sg; > + ret = vu_queue_pop(vdev, vq, &elem[0]); > + if (ret < 0) > + return 0; > + > + if (elem[0].in_num < 1) { > + err("virtio-net receive queue contains no in buffers"); Should this really be err(), or debug()? I wonder if it can happen in bursts. > + vu_queue_rewind(vq, 1); > + return 0; > + } > + > + vh = elem[0].in_sg[0].iov_base; > + > + vh->hdr = vu_header; > + if (vdev->hdrlen == sizeof(struct virtio_net_hdr_mrg_rxbuf)) > + vh->num_buffers = htole16(1); > + > + l2_iov[TCP_IOV_TAP].iov_base = NULL; > + l2_iov[TCP_IOV_TAP].iov_len = 0; > + l2_iov[TCP_IOV_ETH].iov_base = (char *)elem[0].in_sg[0].iov_base + vdev->hdrlen; > + l2_iov[TCP_IOV_ETH].iov_len = sizeof(struct ethhdr); > + > + eh = l2_iov[TCP_IOV_ETH].iov_base; > + > + memcpy(eh->h_dest, c->mac_guest, sizeof(eh->h_dest)); > + memcpy(eh->h_source, c->mac, sizeof(eh->h_source)); > + > + if (CONN_V4(conn)) { > + struct tcp_flags_t *payload; > + struct iphdr *iph; > + uint32_t seq; > + > + l2_iov[TCP_IOV_IP].iov_base = (char *)l2_iov[TCP_IOV_ETH].iov_base + > + l2_iov[TCP_IOV_ETH].iov_len; > + l2_iov[TCP_IOV_IP].iov_len = sizeof(struct iphdr); > + l2_iov[TCP_IOV_PAYLOAD].iov_base = (char *)l2_iov[TCP_IOV_IP].iov_base + > + l2_iov[TCP_IOV_IP].iov_len; > + > + eh->h_proto = htons(ETH_P_IP); > + > + iph = l2_iov[TCP_IOV_IP].iov_base; > + *iph = (struct iphdr)L2_BUF_IP4_INIT(IPPROTO_TCP); > + > + payload = l2_iov[TCP_IOV_PAYLOAD].iov_base; > + payload->th = (struct tcphdr){ > + .doff = offsetof(struct tcp_flags_t, opts) / 4, > + .ack = 1 > + }; > + > + seq = conn->seq_to_tap; > + ret = tcp_prepare_flags(c, conn, flags, &payload->th, payload->opts, &optlen); > + if (ret <= 0) { > + vu_queue_rewind(vq, 1); > + return ret; > + } > + > + l4len = tcp_l2_buf_fill_headers(c, conn, l2_iov, optlen, NULL, > + seq); > + /* keep the following assignment for clarity */ > + /* cppcheck-suppress unreadVariable */ > + l2_iov[TCP_IOV_PAYLOAD].iov_len = l4len; > + > + l2len = l4len + sizeof(*iph) + sizeof(struct ethhdr); > + } else { > + struct tcp_flags_t *payload; > + struct ipv6hdr *ip6h; > + uint32_t seq; > + > + l2_iov[TCP_IOV_IP].iov_base = (char *)l2_iov[TCP_IOV_ETH].iov_base + > + l2_iov[TCP_IOV_ETH].iov_len; > + l2_iov[TCP_IOV_IP].iov_len = sizeof(struct ipv6hdr); > + l2_iov[TCP_IOV_PAYLOAD].iov_base = (char *)l2_iov[TCP_IOV_IP].iov_base + > + l2_iov[TCP_IOV_IP].iov_len; > + > + eh->h_proto = htons(ETH_P_IPV6); > + > + ip6h = l2_iov[TCP_IOV_IP].iov_base; > + *ip6h = (struct ipv6hdr)L2_BUF_IP6_INIT(IPPROTO_TCP); > + > + payload = l2_iov[TCP_IOV_PAYLOAD].iov_base; > + payload->th = (struct tcphdr){ > + .doff = offsetof(struct tcp_flags_t, opts) / 4, > + .ack = 1 > + }; > + > + seq = conn->seq_to_tap; > + ret = tcp_prepare_flags(c, conn, flags, &payload->th, payload->opts, &optlen); > + if (ret <= 0) { > + vu_queue_rewind(vq, 1); > + return ret; > + } > + > + l4len = tcp_l2_buf_fill_headers(c, conn, l2_iov, optlen, NULL, > + seq); > + /* keep the following assignment for clarity */ > + /* cppcheck-suppress unreadVariable */ > + l2_iov[TCP_IOV_PAYLOAD].iov_len = l4len; > + > + l2len = l4len + sizeof(*ip6h) + sizeof(struct ethhdr); > + } > + l2len += vdev->hdrlen; > + ASSERT(l2len <= elem[0].in_sg[0].iov_len); > + > + elem[0].in_sg[0].iov_len = l2len; > + tcp_vu_pcap(c, tapside, &elem[0].in_sg[0], 1, l4len); > + > + vu_queue_fill(vq, &elem[0], l2len, 0); > + nb_ack = 1; > + > + if (flags & DUP_ACK) { > + struct iovec in_sg_dup; > + > + elem[1].out_num = 0; > + elem[1].out_sg = NULL; > + elem[1].in_num = 1; > + elem[1].in_sg = &in_sg_dup; > + ret = vu_queue_pop(vdev, vq, &elem[1]); > + if (ret == 0) { > + if (elem[1].in_num < 1 || elem[1].in_sg[0].iov_len < l2len) { > + vu_queue_rewind(vq, 1); > + } else { > + memcpy(elem[1].in_sg[0].iov_base, vh, l2len); > + nb_ack++; > + > + tcp_vu_pcap(c, tapside, &elem[1].in_sg[0], 1, > + l4len); > + > + vu_queue_fill(vq, &elem[1], l2len, 1); > + } > + } > + } I guess it would be nice one day to decrease code duplication here, but let's keep this simple for the moment. > + > + vu_queue_flush(vq, nb_ack); > + vu_queue_notify(vdev, vq); > + > + return 0; > +} > + > +static ssize_t tcp_vu_sock_recv(struct ctx *c, > + struct tcp_tap_conn *conn, bool v4, > + size_t fillsize, uint16_t mss, > + ssize_t *data_len) Function comment missing. > +{ > + struct vu_dev *vdev = c->vdev; > + struct vu_virtq *vq = &vdev->vq[VHOST_USER_RX_QUEUE]; > + static struct iovec in_sg[VIRTQUEUE_MAX_SIZE]; > + struct msghdr mh_sock = { 0 }; > + static int in_sg_count; > + int s = conn->sock; > + size_t l2_hdrlen; > + int segment_size; > + int iov_cnt; > + ssize_t ret; > + > + l2_hdrlen = tcp_vu_l2_hdrlen(vdev, !v4); > + > + iov_cnt = 0; > + in_sg_count = 0; > + segment_size = 0; > + *data_len = 0; > + while (fillsize > 0 && iov_cnt < VIRTQUEUE_MAX_SIZE - 1 && > + in_sg_count < ARRAY_SIZE(in_sg)) { > + > + elem[iov_cnt].out_num = 0; > + elem[iov_cnt].out_sg = NULL; > + elem[iov_cnt].in_num = ARRAY_SIZE(in_sg) - in_sg_count; > + elem[iov_cnt].in_sg = &in_sg[in_sg_count]; > + ret = vu_queue_pop(vdev, vq, &elem[iov_cnt]); > + if (ret < 0) > + break; > + > + if (elem[iov_cnt].in_num < 1) > + die("virtio-net receive queue contains no in buffers"); We could easily recover (without doing anything) from this error, right? If that's the case, I think this should be a warn(). > + > + in_sg_count += elem[iov_cnt].in_num; > + > + ASSERT(elem[iov_cnt].in_num == 1); > + ASSERT(elem[iov_cnt].in_sg[0].iov_len >= l2_hdrlen); > + > + if (segment_size == 0) { > + iov_vu[iov_cnt + 1].iov_base = > + (char *)elem[iov_cnt].in_sg[0].iov_base + l2_hdrlen; > + iov_vu[iov_cnt + 1].iov_len = > + elem[iov_cnt].in_sg[0].iov_len - l2_hdrlen; > + } else { > + iov_vu[iov_cnt + 1].iov_base = elem[iov_cnt].in_sg[0].iov_base; > + iov_vu[iov_cnt + 1].iov_len = elem[iov_cnt].in_sg[0].iov_len; > + } > + > + if (iov_vu[iov_cnt + 1].iov_len > fillsize) > + iov_vu[iov_cnt + 1].iov_len = fillsize; Perhaps storing an "iov" pointer to iov_vu[iov_cnt + 1] would make this less crowded. > + > + segment_size += iov_vu[iov_cnt + 1].iov_len; > + if (vdev->hdrlen != sizeof(struct virtio_net_hdr_mrg_rxbuf)) { > + segment_size = 0; > + } else if (segment_size >= mss) { > + iov_vu[iov_cnt + 1].iov_len -= segment_size - mss; > + segment_size = 0; > + } > + fillsize -= iov_vu[iov_cnt + 1].iov_len; > + > + iov_cnt++; > + } > + if (iov_cnt == 0) > + return 0; > + > + mh_sock.msg_iov = iov_vu; > + mh_sock.msg_iovlen = iov_cnt + 1; > + > + do > + ret = recvmsg(s, &mh_sock, MSG_PEEK); > + while (ret < 0 && errno == EINTR); > + > + if (ret < 0) { > + vu_queue_rewind(vq, iov_cnt); > + if (errno != EAGAIN && errno != EWOULDBLOCK) { > + ret = -errno; > + tcp_rst(c, conn); > + } > + return ret; > + } > + if (!ret) { Maybe make the non-error path explicit, say, if (ret) goto out; > + vu_queue_rewind(vq, iov_cnt); > + > + if ((conn->events & (SOCK_FIN_RCVD | TAP_FIN_SENT)) == SOCK_FIN_RCVD) { > + int retf = tcp_vu_send_flag(c, conn, FIN | ACK); > + if (retf) { > + tcp_rst(c, conn); > + return retf; > + } > + > + conn_event(c, conn, TAP_FIN_SENT); > + } > + return 0; > + } > + > + *data_len = ret; > + return iov_cnt; > +} > + > +static size_t tcp_vu_prepare(const struct ctx *c, > + struct tcp_tap_conn *conn, struct iovec *first, > + size_t data_len, const uint16_t **check) > +{ Missing function comment. > + const struct flowside *toside = TAPFLOW(conn); > + const struct vu_dev *vdev = c->vdev; > + struct iovec l2_iov[TCP_NUM_IOVS]; > + char *base = first->iov_base; > + struct ethhdr *eh; > + size_t l4len; > + > + /* we guess the first iovec provided by the guest can embed > + * all the headers needed by L2 frame > + */ Mixed tabs and spaces. > + > + l2_iov[TCP_IOV_TAP].iov_base = NULL; > + l2_iov[TCP_IOV_TAP].iov_len = 0; > + l2_iov[TCP_IOV_ETH].iov_base = base + vdev->hdrlen; > + l2_iov[TCP_IOV_ETH].iov_len = sizeof(struct ethhdr); > + > + eh = l2_iov[TCP_IOV_ETH].iov_base; > + > + memcpy(eh->h_dest, c->mac_guest, sizeof(eh->h_dest)); > + memcpy(eh->h_source, c->mac, sizeof(eh->h_source)); > + > + /* initialize header */ > + if (inany_v4(&toside->eaddr) && inany_v4(&toside->faddr)) { > + struct tcp_payload_t *payload; > + struct iphdr *iph; > + > + ASSERT(first[0].iov_len >= vdev->hdrlen + > + sizeof(struct ethhdr) + sizeof(struct iphdr) + > + sizeof(struct tcphdr)); > + > + l2_iov[TCP_IOV_IP].iov_base = (char *)l2_iov[TCP_IOV_ETH].iov_base + > + l2_iov[TCP_IOV_ETH].iov_len; > + l2_iov[TCP_IOV_IP].iov_len = sizeof(struct iphdr); > + l2_iov[TCP_IOV_PAYLOAD].iov_base = (char *)l2_iov[TCP_IOV_IP].iov_base + > + l2_iov[TCP_IOV_IP].iov_len; > + > + > + eh->h_proto = htons(ETH_P_IP); > + > + iph = l2_iov[TCP_IOV_IP].iov_base; > + *iph = (struct iphdr)L2_BUF_IP4_INIT(IPPROTO_TCP); > + payload = l2_iov[TCP_IOV_PAYLOAD].iov_base; > + payload->th = (struct tcphdr){ > + .doff = offsetof(struct tcp_payload_t, data) / 4, > + .ack = 1 > + }; > + > + l4len = tcp_l2_buf_fill_headers(c, conn, l2_iov, > + data_len, *check, > + conn->seq_to_tap); > + /* keep the following assignment for clarity */ > + /* cppcheck-suppress unreadVariable */ > + l2_iov[TCP_IOV_PAYLOAD].iov_len = l4len; > + > + *check = &iph->check; > + } else { > + struct tcp_payload_t *payload; > + struct ipv6hdr *ip6h; > + > + ASSERT(first[0].iov_len >= vdev->hdrlen + > + sizeof(struct ethhdr) + sizeof(struct ipv6hdr) + > + sizeof(struct tcphdr)); > + > + l2_iov[TCP_IOV_IP].iov_base = (char *)l2_iov[TCP_IOV_ETH].iov_base + > + l2_iov[TCP_IOV_ETH].iov_len; > + l2_iov[TCP_IOV_IP].iov_len = sizeof(struct ipv6hdr); > + l2_iov[TCP_IOV_PAYLOAD].iov_base = (char *)l2_iov[TCP_IOV_IP].iov_base + > + l2_iov[TCP_IOV_IP].iov_len; > + > + > + eh->h_proto = htons(ETH_P_IPV6); > + > + ip6h = l2_iov[TCP_IOV_IP].iov_base; > + *ip6h = (struct ipv6hdr)L2_BUF_IP6_INIT(IPPROTO_TCP); > + > + payload = l2_iov[TCP_IOV_PAYLOAD].iov_base; > + payload->th = (struct tcphdr){ > + .doff = offsetof(struct tcp_payload_t, data) / 4, > + .ack = 1 > + }; > +; > + l4len = tcp_l2_buf_fill_headers(c, conn, l2_iov, > + data_len, > + NULL, conn->seq_to_tap); > + /* keep the following assignment for clarity */ > + /* cppcheck-suppress unreadVariable */ > + l2_iov[TCP_IOV_PAYLOAD].iov_len = l4len; > + } > + > + return l4len; > +} I reviewed until here for the moment, the rest will come in a bit. -- Stefano