From nobody Sun Dec 14 06:35:32 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) client-ip=8.43.85.245; envelope-from=devel-bounces@lists.libvirt.org; helo=lists.libvirt.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=fail(p=none dis=none) header.from=smartx.com Return-Path: Received: from lists.libvirt.org (lists.libvirt.org [8.43.85.245]) by mx.zohomail.com with SMTPS id 1751642529361971.1330255611193; Fri, 4 Jul 2025 08:22:09 -0700 (PDT) Received: by lists.libvirt.org (Postfix, from userid 996) id 6E7EE14FE; Fri, 4 Jul 2025 11:22:08 -0400 (EDT) Received: from lists.libvirt.org (localhost [IPv6:::1]) by lists.libvirt.org (Postfix) with ESMTP id 2254115B7; Fri, 4 Jul 2025 11:20:34 -0400 (EDT) Received: by lists.libvirt.org (Postfix, from userid 996) id 3C80C1445; Fri, 4 Jul 2025 11:20:28 -0400 (EDT) Received: from mail-pl1-f175.google.com (mail-pl1-f175.google.com [209.85.214.175]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by lists.libvirt.org (Postfix) with ESMTPS id 41DAE145E for ; Fri, 4 Jul 2025 11:20:26 -0400 (EDT) Received: by mail-pl1-f175.google.com with SMTP id d9443c01a7336-235d6de331fso14133495ad.3 for ; Fri, 04 Jul 2025 08:20:26 -0700 (PDT) Received: from Hyman-Dev-Euler.zelin.local ([45.8.186.102]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-23c84350221sm23628205ad.83.2025.07.04.08.20.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 04 Jul 2025 08:20:24 -0700 (PDT) X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on lists.libvirt.org X-Spam-Level: X-Spam-Status: No, score=-0.8 required=5.0 tests=DKIM_INVALID,DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2,RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED,SPF_HELO_NONE autolearn=unavailable autolearn_force=no version=3.4.4 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=smartx-com.20230601.gappssmtp.com; s=20230601; t=1751642425; x=1752247225; darn=lists.libvirt.org; 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; bh=y1TLyi+SVZr7mnLuuXipiC8II3zUAkGKVFHc9cwRfhA=; b=AY4Ac6HKC6Whl3Dua8DJAgGe/jRld3r3L8tNxn/DAM+8kATA3Tat0KKF+zCCUlGJ0r tYoc7Slz5pPRZWXAGm6JPQ5eGkldmg2fiZwUN0cg7gg3e0/dqis6vgPSoWgkA9RUTDhZ +DMpLSfLk0HaPFi98Ozcc5O8J1VvDiqMX08KVc6SnbVaMnbyRipyrHgu2y+lTyDYtYS8 58Qo3o0KNnNFvxVtVipYbhDA35ofNi0/PEhouZaBVnZlt2IOZBV9xC57JE0FgOrJvqBa skxUQvFCf9kbUO9Rdgh9SZLrF1kBdC6JrPNjAaLMA4Bc317YPK6MVlTTC6n1318wAPIx RLvA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1751642425; x=1752247225; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=y1TLyi+SVZr7mnLuuXipiC8II3zUAkGKVFHc9cwRfhA=; b=vIlhFqucg4QIEQMQ3iJyBoGnCZXNI5WqLbaeDsSlJgfGNX0l/QP9DWNYzcf4tns88l MKcc6Pq7XVLhijPdE1baHyAxAcvmsu6VLvS3X+UiqhuD/pozuoe8zD9XuQdh+K9zL+Fc wEYtR8tK9Pl8OnT46HvGAZ78ucvR1J5p5IAJEFdQfbXyyrGMDnWH/5juh75e06VoAvqQ tIbIypEnqdw0/P6ieH0oo5kgHhoetoympccY+CjIavl6/Ceagv+j2p6WbpR5PFXMZpvT FerCAYwqLZictEcAX3cFptv3Ddmnl9n8borZNynPlPS7ijZvUMQHKk5wNIX10G4gtfg5 PIZA== X-Gm-Message-State: AOJu0YxpFQBvSeld60aZi4f+7OHKBxlc0+2IuYBpIxlzNg+zwVvpaN9N 38P/pQtgRRFAK/235vmQCkcDswksGWkwtYxJfsoSbvEuwkzzJZPWOyC72sLESaUD97RZ+uy8hgq wMI8RIGmgpQ== X-Gm-Gg: ASbGncuSVb2mgHsAqVLeqZ9dI/2ZChcC+3MlaI13VXukCgVnCwUHzhaYwiiORPImBxi C9gytYuspio823iHjCV6AHQp3lkqoPeKXvFL6ou0zftCpdCUsJZdmde5UnArzBiR33WdBAHhDaK EgR0BKck0GAG2dAePVyHd0KecDXUkFYhNCwPAzU4+CFt2GpnZ0M8kIkVFXvdHTCY1pfbaPH+17t jlE3kOAs0Mh4IHBXdxgBVEbfyDHUw7K2yLBJdlx01yRJxpeNx63+LsZ7mS3+r2M9oe6iSM/Gdst gkf2BOvbLt4qp/s7RzwRL3X/Z5WA9Uvwhxo0V504VjIGHPjUQYm5xNkathmuVHY8ABlhVS019UG rZVszrtRA X-Google-Smtp-Source: AGHT+IF7ErNWBw4Y3g+YcTz6tu1YD2s6mUoMXnx6r/QpwXPJRpxHilcO8Edm14fBpUet43+lM5fEtw== X-Received: by 2002:a17:902:d592:b0:235:799:eca5 with SMTP id d9443c01a7336-23c85eb051dmr53673945ad.44.1751642424795; Fri, 04 Jul 2025 08:20:24 -0700 (PDT) From: yong.huang@smartx.com To: devel@lists.libvirt.org Subject: [RFC 2/4] conf: Introduce autoiface attribute for vhostuser interface Date: Fri, 4 Jul 2025 22:57:57 +0800 Message-Id: <9a6b705367e32dc18f7bcd0ced1fcffd5a1b4187.1751640926.git.yong.huang@smartx.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Message-ID-Hash: YUAE6PG7OGOVGO63QGCSY5WLXUCVOGXQ X-Message-ID-Hash: YUAE6PG7OGOVGO63QGCSY5WLXUCVOGXQ X-MailFrom: yong.huang@smartx.com X-Mailman-Rule-Hits: nonmember-moderation X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-config-1; header-match-config-2; header-match-config-3; header-match-devel.lists.libvirt.org-0 CC: yong.huang@smartx.com X-Mailman-Version: 3.2.2 Precedence: list List-Id: Development discussions about the libvirt library & tools Archived-At: List-Archive: List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1751642530923116600 Content-Type: text/plain; charset="utf-8" From: Hyman Huang This implements XML support for automatically attaching a vhostuser port to an Open vSwitch bridge. Here is an example of the config for a vhostuser interface that attached to bridge automatically: This setup offers an automated method to configure a vhostuser interface in server mode, simplifying integration with DPDK-enabled Open vSwitch bridges. --- docs/formatdomain.rst | 37 +++++++++++- src/conf/domain_conf.c | 59 ++++++++++++++++++- src/conf/domain_conf.h | 6 ++ src/conf/domain_postparse.c | 46 +++++++++++++++ src/conf/domain_validate.c | 24 ++++++++ src/conf/schemas/domaincommon.rng | 20 +++++++ .../net-vhostuser.x86_64-latest.xml | 10 ++++ tests/qemuxmlconfdata/net-vhostuser.xml | 10 ++++ 8 files changed, 209 insertions(+), 3 deletions(-) diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 9a2f065590..d7051618a7 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -6533,6 +6533,12 @@ plane is based on shared memory. + + + + + + @@ -6557,11 +6563,40 @@ two attributes ``enabled`` (which accepts ``yes`` a= nd ``no``) and ``timeout`` which specifies the amount of seconds after which hypervisor tries to reconnect. =20 +The ```` element specifies the backend implementation of +the ``vhostuser`` interface type. The ``type`` attribute is required +and currently supports ``openvswitch`` and ``passt``. + +If ``type=3D'openvswitch'``: + + * The ``autoiface`` attribute may be specified (``yes`` or ``no``). + * If ``autoiface=3D'yes'``, the ```` element ``bridge`` + attribute is mandatory. Libvirt will derive the interface name + by extracting the substring after the ``/`` character in the + vhostuser server path, and attach it to the bridge specified by + the ``bridge`` attribute. + +If ``type=3D'passt'``: + + * See the `here + `__ + section for detailed information. + +The optional ```` element applies only to the +``openvswitch`` backend (which is both the default and only supported +type currently). See the `here +`__ section for detailed information +about ````. + Note that when ``mode=3D'server'`` is used, the hypervisor will wait for t= he incoming connection to be established prior to actually running the VM. Th= is is not possible when hotplugging an interface with such config so the VM will continue to run even when no connection is made. It's advised to use -``mode=3D'client'`` instead. +``mode=3D'client'`` instead. :since:`Since 11.6.0`, Libvirt can automatica= lly +attach the vhostuser port to the Open vSwitch bridge (i.e., the ``backend`` +element has ``type=3D'openvswitch'`` and ``autoiface=3D'yes'``). This setup +offers an automated method to configure a vhostuser interface in server +mode, simplifying integration with DPDK-enabled Open vSwitch bridges. =20 vhost-user connection with passt backend ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 9862a76023..2b0b840819 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -650,6 +650,7 @@ VIR_ENUM_IMPL(virDomainNetBackend, VIR_DOMAIN_NET_BACKEND_LAST, "default", "passt", + "openvswitch", ); =20 VIR_ENUM_IMPL(virDomainNetProto, @@ -9773,6 +9774,8 @@ virDomainNetBackendParseXML(xmlNodePtr node, { g_autofree char *tap =3D virXMLPropString(node, "tap"); g_autofree char *vhost =3D virXMLPropString(node, "vhost"); + virTristateBool autoiface; + xmlNodePtr parameters =3D NULL; =20 /* In the case of NET_TYPE_USER, backend type can be unspecified * (i.e. VIR_DOMAIN_NET_BACKEND_DEFAULT) and that means 'use @@ -9798,8 +9801,33 @@ virDomainNetBackendParseXML(xmlNodePtr node, return -1; } =20 - if (def->backend.type =3D=3D VIR_DOMAIN_NET_BACKEND_PASST) + switch (def->backend.type) { + case VIR_DOMAIN_NET_BACKEND_DEFAULT: + break; + + case VIR_DOMAIN_NET_BACKEND_PASST: def->backend.data.passt.logFile =3D virXMLPropString(node, "logFil= e"); + break; + + case VIR_DOMAIN_NET_BACKEND_OPENVSWITCH: + if (virXMLPropTristateBool(node, "autoiface", VIR_XML_PROP_NONE, + &autoiface) < 0) + return -1; + virTristateBoolToBool(autoiface, &def->backend.data.openvswitch.au= toiface); + + if (def->backend.data.openvswitch.autoiface) { + if ((parameters =3D virXMLNodeGetSubelement(node, "parameters"= ))) { + def->backend.data.openvswitch.iface =3D virXMLPropString(p= arameters, "iface"); + def->backend.data.openvswitch.bridge =3D virXMLPropString(= parameters, "bridge"); + } + } + break; + + case VIR_DOMAIN_NET_BACKEND_LAST: + default: + virReportEnumRangeError(virDomainNetBackendType, def->backend.type= ); + break; + } =20 if (tap) def->backend.tap =3D virFileSanitizePath(tap); @@ -10142,6 +10170,10 @@ virDomainNetDefParseXML(virDomainXMLOption *xmlopt, source_node, ctxt) = < 0) return NULL; } + + virtualport_flags =3D VIR_VPORT_XML_GENERATE_MISSING_DEFAULTS | + VIR_VPORT_XML_REQUIRE_ALL_ATTRIBUTES | + VIR_VPORT_XML_REQUIRE_TYPE; } break; =20 @@ -24927,11 +24959,26 @@ virDomainNetTeamingInfoFormat(virDomainNetTeaming= Info *teaming, } =20 =20 +static void +virDomainNetBackendOpenvswitchIfaceFormat(virDomainNetBackend *backend, + virBuffer *buf) +{ + if (backend->data.openvswitch.autoiface) { + virBufferEscapeString(buf, "data.openvswitch.iface); + virBufferEscapeString(buf, " bridge=3D'%s'", + backend->data.openvswitch.bridge); + virBufferAddLit(buf, "/>\n"); + } +} + + static void virDomainNetBackendFormat(virBuffer *buf, virDomainNetBackend *backend) { g_auto(virBuffer) attrBuf =3D VIR_BUFFER_INITIALIZER; + g_auto(virBuffer) childBuf =3D VIR_BUFFER_INIT_CHILD(buf); =20 if (backend->type) { virBufferAsprintf(&attrBuf, " type=3D'%s'", @@ -24941,8 +24988,12 @@ virDomainNetBackendFormat(virBuffer *buf, virBufferEscapeString(&attrBuf, " vhost=3D'%s'", backend->vhost); if (backend->type =3D=3D VIR_DOMAIN_NET_BACKEND_PASST) { virBufferEscapeString(&attrBuf, " logFile=3D'%s'", backend->data.p= asst.logFile); + } else if (backend->type =3D=3D VIR_DOMAIN_NET_BACKEND_OPENVSWITCH) { + virBufferAsprintf(&attrBuf, " autoiface=3D'%s'", + backend->data.openvswitch.autoiface ? "yes" : "n= o"); + virDomainNetBackendOpenvswitchIfaceFormat(backend, &childBuf); } - virXMLFormatElement(buf, "backend", &attrBuf, NULL); + virXMLFormatElement(buf, "backend", &attrBuf, &childBuf); } =20 =20 @@ -30141,6 +30192,10 @@ virDomainNetGetActualBridgeName(const virDomainNet= Def *iface) (iface->data.network.actual->type =3D=3D VIR_DOMAIN_NET_TYPE_BRIDG= E || iface->data.network.actual->type =3D=3D VIR_DOMAIN_NET_TYPE_NETWO= RK)) return iface->data.network.actual->data.bridge.brname; + if (iface->type =3D=3D VIR_DOMAIN_NET_TYPE_VHOSTUSER && + iface->backend.type =3D=3D VIR_DOMAIN_NET_BACKEND_OPENVSWITCH && + iface->backend.data.openvswitch.autoiface) + return iface->backend.data.openvswitch.bridge; return NULL; } =20 diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 6d43654f0a..c19a56e375 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1030,6 +1030,7 @@ typedef enum { typedef enum { VIR_DOMAIN_NET_BACKEND_DEFAULT =3D 0, VIR_DOMAIN_NET_BACKEND_PASST, + VIR_DOMAIN_NET_BACKEND_OPENVSWITCH, =20 VIR_DOMAIN_NET_BACKEND_LAST } virDomainNetBackendType; @@ -1078,6 +1079,11 @@ struct _virDomainNetBackend { struct { char *logFile; /* path to logfile used by passt process */ } passt; + struct { + bool autoiface; /* create openvswitch interface automatically = */ + char *iface; /* interface name */ + char *bridge; /* bridge name that interface attached to */ + } openvswitch; } data; }; =20 diff --git a/src/conf/domain_postparse.c b/src/conf/domain_postparse.c index a07ec8d94e..f618ea3165 100644 --- a/src/conf/domain_postparse.c +++ b/src/conf/domain_postparse.c @@ -1235,6 +1235,49 @@ virDomainDefRejectDuplicatePanics(virDomainDef *def) } =20 =20 +static int +virDomainPostParseInterfaceAutoGenerate(virDomainNetDef *net) +{ + if (net->type =3D=3D VIR_DOMAIN_NET_TYPE_VHOSTUSER && + net->backend.type =3D=3D VIR_DOMAIN_NET_BACKEND_OPENVSWITCH && + net->backend.data.openvswitch.autoiface) { + if (!net->virtPortProfile) { + virNetDevVPortProfile *virtPort =3D g_new0(virNetDevVPortProfi= le, 1); + virtPort->virtPortType =3D VIR_NETDEV_VPORT_PROFILE_OPENVSWITC= H; + if (virUUIDGenerate(virtPort->instanceID) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot generate a random uuid for instan= ceid")); + return -1; + } + virtPort->instanceID_specified =3D true; + net->virtPortProfile =3D virtPort; + } + + if (!net->backend.data.openvswitch.iface) { + virDomainChrSourceDef *src =3D net->data.vhostuser; + char *ifname =3D strrchr(src->data.nix.path, '/'); + if (++ifname) { + net->backend.data.openvswitch.iface =3D g_strdup(ifname); + } + } + } + + return 0; +} + + +static int +virDomainDefPostParseInterface(virDomainDef *def) +{ + size_t i; + for (i =3D 0; i < def->nnets; i++) { + if (virDomainPostParseInterfaceAutoGenerate(def->nets[i]) < 0) + return -1; + } + return 0; +} + + static int virDomainDefPostParseCommon(virDomainDef *def, struct virDomainDefPostParseDeviceIteratorData= *data, @@ -1315,6 +1358,9 @@ virDomainDefPostParseCommon(virDomainDef *def, if (virDomainDefPostParseCPU(def) < 0) return -1; =20 + if (virDomainDefPostParseInterface(def) < 0) + return -1; + return 0; } =20 diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c index b28af7fa56..03ebce93a1 100644 --- a/src/conf/domain_validate.c +++ b/src/conf/domain_validate.c @@ -2262,6 +2262,30 @@ virDomainNetDefValidate(const virDomainNetDef *net) } } =20 + if (net->backend.type =3D=3D VIR_DOMAIN_NET_BACKEND_OPENVSWITCH) { + if (net->type !=3D VIR_DOMAIN_NET_TYPE_VHOSTUSER) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("The 'openvswitch' backend can only be used w= ith interface type=3D'vhostuser'")); + return -1; + } + + if (net->backend.data.openvswitch.autoiface) { + if (!net->backend.data.openvswitch.iface || + !net->backend.data.openvswitch.bridge) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("No interface or bridge name provided")); + return -1; + } + + if ((!net->virtPortProfile) || + (net->virtPortProfile->virtPortType !=3D VIR_NETDEV_VPORT_= PROFILE_OPENVSWITCH)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Invalid virtualport element encountered"= )); + return -1; + } + } + } + if (net->sourceDev && net->backend.type !=3D VIR_DOMAIN_NET_BACKEND_PA= SST) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("The 'dev' attribute of the element can = only be used with type=3D'user' or type=3D'vhostuser' if the type=3D'passt'")); diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincom= mon.rng index 183dd5db5e..50a37fce43 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -3534,6 +3534,9 @@ + + + @@ -3913,6 +3916,7 @@ default passt + openvswitch @@ -3931,6 +3935,22 @@ + + + + + + + + + + + [a-zA-Z0-9\-_]+ + + + + + diff --git a/tests/qemuxmlconfdata/net-vhostuser.x86_64-latest.xml b/tests/= qemuxmlconfdata/net-vhostuser.x86_64-latest.xml index 44bebef2c8..05b85a6df0 100644 --- a/tests/qemuxmlconfdata/net-vhostuser.x86_64-latest.xml +++ b/tests/qemuxmlconfdata/net-vhostuser.x86_64-latest.xml @@ -30,12 +30,22 @@ + + + +
+ + + + + +
diff --git a/tests/qemuxmlconfdata/net-vhostuser.xml b/tests/qemuxmlconfdat= a/net-vhostuser.xml index 91d1abc027..dc4d69ba2b 100644 --- a/tests/qemuxmlconfdata/net-vhostuser.xml +++ b/tests/qemuxmlconfdata/net-vhostuser.xml @@ -23,11 +23,21 @@ + + + + + + + + + + --=20 2.27.0