From nobody Mon Feb 9 12:15:10 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=gmail.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1638809066767326.6645180233246; Mon, 6 Dec 2021 08:44:26 -0800 (PST) Received: from localhost ([::1]:34420 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1muH6H-0007SU-LB for importer@patchew.org; Mon, 06 Dec 2021 11:44:25 -0500 Received: from eggs.gnu.org ([209.51.188.92]:33248) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1muH4b-0004h6-VL for qemu-devel@nongnu.org; Mon, 06 Dec 2021 11:42:41 -0500 Received: from [2a00:1450:4864:20::22c] (port=40941 helo=mail-lj1-x22c.google.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1muH4Z-00074o-6l for qemu-devel@nongnu.org; Mon, 06 Dec 2021 11:42:41 -0500 Received: by mail-lj1-x22c.google.com with SMTP id u22so22082819lju.7 for ; Mon, 06 Dec 2021 08:42:38 -0800 (PST) Received: from UNIT-808.labs.intellij.net ([91.132.204.19]) by smtp.gmail.com with ESMTPSA id e11sm1401809lfq.65.2021.12.06.08.42.36 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Mon, 06 Dec 2021 08:42:36 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=UooPoY0MVV/Y1dF1WCBxe5LUUmT/aqG+eGQmvWF3yb0=; b=e6AeLuAePwWJjEBa2gKiCVT4B8gE+25Om2ypO5ZR/52pCatY2f+CuW8lC7YKAs7hG8 b1wVydlcqI6xCj6DgdPBExussyFlpXVr8L/YqMyJ07OFuSj9eOHs0CMcFN/cUaB8G5wi ZD2t4LBXJ664RcJ1k9mUa47vOgBmkJHZxKkh7NUivZ5M7sI+TI5jBsujOqAT5eQKLGvv 6qc3rMxrPkc57UDZttxW+8BAa9mws7TtaAlxG85Em1JiNlrJEdcM3yhI1eMPgJR1WVNU nfnkD31clcQVmM5/Ztz5zpXUqkdnxZonTzqqIIjv2iZUi+aJLwh9ipBODYLo83lOSh2y zBaA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=UooPoY0MVV/Y1dF1WCBxe5LUUmT/aqG+eGQmvWF3yb0=; b=SSNe8Rnq2zDKSxTf6yGvaOmdfc9OxoAmk/3kwp9LWMDJ7tOzD5TIFbiAYUlj2g+443 ZE5Jtotm7P6eSi64IIaW2JAELrbMnj0IzW24Pj0oXXGQ7wUpQXkstjJnvAc+axM0NLCg KYg1Ij37n/u0uHEKJ6WWL3g79mLfNNVN06wJ1lkXMUSPDNondSdHeR0U4buPOHsctzys D56q+/e0M0VpeYsBSLCbMPjwZvHQv9GYhMnc9A3+d5VtURVIxzK3wmlxHhehDErIN1Li 5khrfhghPGVIvM4cpkup63B2lklIAo2nHUP1RzMooUXIqZbIMPRVve12r6vADImVciWE ygbg== X-Gm-Message-State: AOAM531n3/OKFgD3BErDRr0WOWKv8f3gniawr24Mx+YIPMbselru/zIi dEv8D6J1949Gn2wCw9gZpSKsUzz+Z3m/YXyr X-Google-Smtp-Source: ABdhPJxEl7TjqjOKEvLufALpji6RKwonpYABp85G3ig8s4g4MlJQ4/6OCaDSn530MlPDNorlNIjFHw== X-Received: by 2002:a2e:a407:: with SMTP id p7mr35915789ljn.468.1638808957292; Mon, 06 Dec 2021 08:42:37 -0800 (PST) From: Vladislav Yaroshchuk To: qemu-devel@nongnu.org Subject: [PATCH v6 3/6] net/vmnet: implement shared mode (vmnet-shared) Date: Mon, 6 Dec 2021 19:42:19 +0300 Message-Id: <20211206164222.11027-4-yaroshchuk2000@gmail.com> X-Mailer: git-send-email 2.23.0 In-Reply-To: <20211206164222.11027-1-yaroshchuk2000@gmail.com> References: <20211206164222.11027-1-yaroshchuk2000@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Host-Lookup-Failed: Reverse DNS lookup failed for 2a00:1450:4864:20::22c (failed) Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2a00:1450:4864:20::22c; envelope-from=yaroshchuk2000@gmail.com; helo=mail-lj1-x22c.google.com X-Spam_score_int: -10 X-Spam_score: -1.1 X-Spam_bar: - X-Spam_report: (-1.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_ENVFROM_END_DIGIT=0.25, FREEMAIL_FROM=0.001, PDS_HP_HELO_NORDNS=0.001, RCVD_IN_DNSWL_NONE=-0.0001, RDNS_NONE=0.793, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Vladislav Yaroshchuk , jasowang@redhat.com, phillip.ennen@gmail.com, armbru@redhat.com, r.bolshakov@yadro.com, phillip@axleos.com, akihiko.odaki@gmail.com, hsp.cat7@gmail.com, hello@adns.io, eblake@redhat.com Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1638809067824100005 Content-Type: text/plain; charset="utf-8" Interaction with vmnet.framework in different modes differs only on configuration stage, so we can create common `send`, `receive`, etc. procedures and reuse them. vmnet.framework supports iov, but writing more than one iov into vmnet interface fails with 'VMNET_INVALID_ARGUMENT'. Collecting provided iovs into one and passing it to vmnet works fine. That's the reason why receive_iov() left unimplemented. But it still works with good enough performance having .receive() implemented only. Also, there is no way to unsubscribe from vmnet packages receiving except registering and unregistering event callback or simply drop packages just ignoring and not processing them when related flag is set. Here we do using the second way. Signed-off-by: Phillip Tennen Signed-off-by: Vladislav Yaroshchuk --- net/vmnet-common.m | 310 +++++++++++++++++++++++++++++++++++++++++++++ net/vmnet-shared.c | 75 ++++++++++- net/vmnet_int.h | 23 ++++ 3 files changed, 404 insertions(+), 4 deletions(-) diff --git a/net/vmnet-common.m b/net/vmnet-common.m index 532d152840..6d474af4be 100644 --- a/net/vmnet-common.m +++ b/net/vmnet-common.m @@ -10,6 +10,8 @@ */ =20 #include "qemu/osdep.h" +#include "qemu/main-loop.h" +#include "qemu/log.h" #include "qapi/qapi-types-net.h" #include "vmnet_int.h" #include "clients.h" @@ -17,4 +19,312 @@ #include "qapi/error.h" =20 #include +#include =20 +#ifdef DEBUG +#define D(x) x +#define D_LOG(...) qemu_log(__VA_ARGS__) +#else +#define D(x) do { } while (0) +#define D_LOG(...) do { } while (0) +#endif + +typedef struct vmpktdesc vmpktdesc_t; +typedef struct iovec iovec_t; + +static void vmnet_set_send_enabled(VmnetCommonState *s, bool enable) +{ + s->send_enabled =3D enable; +} + + +static void vmnet_send_completed(NetClientState *nc, ssize_t len) +{ + VmnetCommonState *s =3D DO_UPCAST(VmnetCommonState, nc, nc); + vmnet_set_send_enabled(s, true); +} + + +static void vmnet_send(NetClientState *nc, + interface_event_t event_id, + xpc_object_t event) +{ + assert(event_id =3D=3D VMNET_INTERFACE_PACKETS_AVAILABLE); + + VmnetCommonState *s; + uint64_t packets_available; + + struct iovec *iov; + struct vmpktdesc *packets; + int pkt_cnt; + int i; + + vmnet_return_t if_status; + ssize_t size; + + s =3D DO_UPCAST(VmnetCommonState, nc, nc); + + packets_available =3D xpc_dictionary_get_uint64( + event, + vmnet_estimated_packets_available_key + ); + + pkt_cnt =3D (packets_available < VMNET_PACKETS_LIMIT) ? + packets_available : + VMNET_PACKETS_LIMIT; + + + iov =3D s->iov_buf; + packets =3D s->packets_buf; + + for (i =3D 0; i < pkt_cnt; ++i) { + packets[i].vm_pkt_size =3D s->max_packet_size; + packets[i].vm_pkt_iovcnt =3D 1; + packets[i].vm_flags =3D 0; + } + + if_status =3D vmnet_read(s->vmnet_if, packets, &pkt_cnt); + if (if_status !=3D VMNET_SUCCESS) { + error_printf("vmnet: read failed: %s\n", + vmnet_status_map_str(if_status)); + } + qemu_mutex_lock_iothread(); + for (i =3D 0; i < pkt_cnt; ++i) { + size =3D qemu_send_packet_async(nc, + iov[i].iov_base, + packets[i].vm_pkt_size, + vmnet_send_completed); + if (size =3D=3D 0) { + vmnet_set_send_enabled(s, false); + } else if (size < 0) { + break; + } + } + qemu_mutex_unlock_iothread(); + +} + + +static void vmnet_register_event_callback(VmnetCommonState *s) +{ + dispatch_queue_t avail_pkt_q =3D dispatch_queue_create( + "org.qemu.vmnet.if_queue", + DISPATCH_QUEUE_SERIAL + ); + + vmnet_interface_set_event_callback( + s->vmnet_if, + VMNET_INTERFACE_PACKETS_AVAILABLE, + avail_pkt_q, + ^(interface_event_t event_id, xpc_object_t event) { + if (s->send_enabled) { + vmnet_send(&s->nc, event_id, event); + } + }); +} + + +static void vmnet_bufs_init(VmnetCommonState *s) +{ + int i; + struct vmpktdesc *packets; + struct iovec *iov; + + packets =3D s->packets_buf; + iov =3D s->iov_buf; + + for (i =3D 0; i < VMNET_PACKETS_LIMIT; ++i) { + iov[i].iov_len =3D s->max_packet_size; + iov[i].iov_base =3D g_malloc0(iov[i].iov_len); + packets[i].vm_pkt_iov =3D iov + i; + } +} + + +const char *vmnet_status_map_str(vmnet_return_t status) +{ + switch (status) { + case VMNET_SUCCESS: + return "success"; + case VMNET_FAILURE: + return "general failure"; + case VMNET_MEM_FAILURE: + return "memory allocation failure"; + case VMNET_INVALID_ARGUMENT: + return "invalid argument specified"; + case VMNET_SETUP_INCOMPLETE: + return "interface setup is not complete"; + case VMNET_INVALID_ACCESS: + return "invalid access, permission denied"; + case VMNET_PACKET_TOO_BIG: + return "packet size is larger than MTU"; + case VMNET_BUFFER_EXHAUSTED: + return "buffers exhausted in kernel"; + case VMNET_TOO_MANY_PACKETS: + return "packet count exceeds limit"; + case VMNET_SHARING_SERVICE_BUSY: + return "conflict, sharing service is in use"; + default: + return "unknown vmnet error"; + } +} + + +int vmnet_if_create(NetClientState *nc, + xpc_object_t if_desc, + Error **errp, + void (*completion_callback)(xpc_object_t interface_par= am)) +{ + VmnetCommonState *s; + + dispatch_queue_t if_create_q; + dispatch_semaphore_t if_created_sem; + + __block vmnet_return_t if_status; + + if_create_q =3D dispatch_queue_create("org.qemu.vmnet.create", + DISPATCH_QUEUE_SERIAL); + if_created_sem =3D dispatch_semaphore_create(0); + + xpc_dictionary_set_bool( + if_desc, + vmnet_allocate_mac_address_key, + false + ); + + D(D_LOG("vmnet.start.interface_desc:\n"); + xpc_dictionary_apply(if_desc, + ^bool(const char *k, xpc_object_t v) { + char *desc =3D xpc_copy_description(v); + D_LOG(" %s=3D%s\n", k, desc); + free(desc); + return true; + })); + + s =3D DO_UPCAST(VmnetCommonState, nc, nc); + s->vmnet_if =3D vmnet_start_interface( + if_desc, + if_create_q, + ^(vmnet_return_t status, xpc_object_t interface_param) { + if_status =3D status; + if (status !=3D VMNET_SUCCESS || !interface_param) { + dispatch_semaphore_signal(if_created_sem); + return; + } + + D(D_LOG("vmnet.start.interface_param:\n"); + xpc_dictionary_apply(interface_param, + ^bool(const char *k, xpc_object_t v) { + char *desc =3D xpc_copy_description= (v); + D_LOG(" %s=3D%s\n", k, desc); + free(desc); + return true; + })); + + s->mtu =3D xpc_dictionary_get_uint64( + interface_param, + vmnet_mtu_key); + s->max_packet_size =3D xpc_dictionary_get_uint64( + interface_param, + vmnet_max_packet_size_key); + + if (completion_callback) { + completion_callback(interface_param); + } + dispatch_semaphore_signal(if_created_sem); + }); + + if (s->vmnet_if =3D=3D NULL) { + error_setg(errp, "unable to create interface with requested params= "); + return -1; + } + + dispatch_semaphore_wait(if_created_sem, DISPATCH_TIME_FOREVER); + dispatch_release(if_create_q); + + if (if_status !=3D VMNET_SUCCESS) { + error_setg(errp, + "cannot create vmnet interface: %s", + vmnet_status_map_str(if_status)); + return -1; + } + + vmnet_register_event_callback(s); + vmnet_bufs_init(s); + vmnet_set_send_enabled(s, true); + + return 0; +} + + +ssize_t vmnet_receive_common(NetClientState *nc, + const uint8_t *buf, + size_t size) +{ + VmnetCommonState *s; + vmpktdesc_t packet; + iovec_t iov; + int pkt_cnt; + vmnet_return_t if_status; + + s =3D DO_UPCAST(VmnetCommonState, nc, nc); + + if (size > s->max_packet_size) { + warn_report("vmnet: packet is too big, %zu > %llu\n", + packet.vm_pkt_size, + s->max_packet_size); + return -1; + } + + iov.iov_base =3D (char *) buf; + iov.iov_len =3D size; + + packet.vm_pkt_iovcnt =3D 1; + packet.vm_flags =3D 0; + packet.vm_pkt_size =3D size; + packet.vm_pkt_iov =3D &iov; + + pkt_cnt =3D 1; + if_status =3D vmnet_write(s->vmnet_if, &packet, &pkt_cnt); + + if (if_status !=3D VMNET_SUCCESS) { + error_report("vmnet: write error: %s\n", + vmnet_status_map_str(if_status)); + } + + if (if_status =3D=3D VMNET_SUCCESS && pkt_cnt) { + return size; + } + return 0; +} + + +void vmnet_cleanup_common(NetClientState *nc) +{ + VmnetCommonState *s; + dispatch_queue_t if_destroy_q; + + s =3D DO_UPCAST(VmnetCommonState, nc, nc); + + qemu_purge_queued_packets(nc); + vmnet_set_send_enabled(s, false); + + if (s->vmnet_if =3D=3D NULL) { + return; + } + + if_destroy_q =3D dispatch_queue_create( + "org.qemu.vmnet.destroy", + DISPATCH_QUEUE_SERIAL + ); + + vmnet_stop_interface( + s->vmnet_if, + if_destroy_q, + ^(vmnet_return_t status) { + }); + + for (int i =3D 0; i < VMNET_PACKETS_LIMIT; ++i) { + g_free(s->iov_buf[i].iov_base); + } +} diff --git a/net/vmnet-shared.c b/net/vmnet-shared.c index f8c4a4f3b8..08f7004427 100644 --- a/net/vmnet-shared.c +++ b/net/vmnet-shared.c @@ -10,16 +10,83 @@ =20 #include "qemu/osdep.h" #include "qapi/qapi-types-net.h" +#include "qapi/error.h" #include "vmnet_int.h" #include "clients.h" -#include "qemu/error-report.h" -#include "qapi/error.h" =20 #include =20 +typedef struct VmnetSharedState { + VmnetCommonState cs; +} VmnetSharedState; + + +static xpc_object_t create_if_desc(const Netdev *netdev, Error **errp) +{ + const NetdevVmnetSharedOptions *options =3D &(netdev->u.vmnet_shared); + xpc_object_t if_desc =3D xpc_dictionary_create(NULL, NULL, 0); + + xpc_dictionary_set_uint64( + if_desc, + vmnet_operation_mode_key, + VMNET_SHARED_MODE + ); + + xpc_dictionary_set_bool( + if_desc, + vmnet_enable_isolation_key, + options->isolated + ); + + if (options->has_nat66_prefix) { + xpc_dictionary_set_string(if_desc, + vmnet_nat66_prefix_key, + options->nat66_prefix); + } + + if (options->has_start_address || + options->has_end_address || + options->has_subnet_mask) { + + if (options->has_start_address && + options->has_end_address && + options->has_subnet_mask) { + + xpc_dictionary_set_string(if_desc, + vmnet_start_address_key, + options->start_address); + xpc_dictionary_set_string(if_desc, + vmnet_end_address_key, + options->end_address); + xpc_dictionary_set_string(if_desc, + vmnet_subnet_mask_key, + options->subnet_mask); + } else { + error_setg( + errp, + "'start-address', 'end-address', 'subnet_mask' " + "should be provided together" + ); + } + } + + return if_desc; +} + +static NetClientInfo net_vmnet_shared_info =3D { + .type =3D NET_CLIENT_DRIVER_VMNET_SHARED, + .size =3D sizeof(VmnetSharedState), + .receive =3D vmnet_receive_common, + .cleanup =3D vmnet_cleanup_common, +}; + int net_init_vmnet_shared(const Netdev *netdev, const char *name, NetClientState *peer, Error **errp) { - error_setg(errp, "vmnet-shared is not implemented yet"); - return -1; + NetClientState *nc =3D qemu_new_net_client(&net_vmnet_shared_info, + peer, "vmnet-shared", name); + xpc_object_t if_desc =3D create_if_desc(netdev, errp); + + return vmnet_if_create(nc, if_desc, errp, NULL); } + diff --git a/net/vmnet_int.h b/net/vmnet_int.h index c5982259a4..3979fe4678 100644 --- a/net/vmnet_int.h +++ b/net/vmnet_int.h @@ -16,10 +16,33 @@ =20 #include =20 +#define VMNET_PACKETS_LIMIT 50 + typedef struct VmnetCommonState { NetClientState nc; + interface_ref vmnet_if; + + bool send_enabled; + + uint64_t mtu; + uint64_t max_packet_size; + + struct vmpktdesc packets_buf[VMNET_PACKETS_LIMIT]; + struct iovec iov_buf[VMNET_PACKETS_LIMIT]; =20 } VmnetCommonState; =20 +const char *vmnet_status_map_str(vmnet_return_t status); + +int vmnet_if_create(NetClientState *nc, + xpc_object_t if_desc, + Error **errp, + void (*completion_callback)(xpc_object_t interface_par= am)); + +ssize_t vmnet_receive_common(NetClientState *nc, + const uint8_t *buf, + size_t size); + +void vmnet_cleanup_common(NetClientState *nc); =20 #endif /* VMNET_INT_H */ --=20 2.23.0