[PATCH 0/7] support <teaming> (aka "QEMU virtio failover") with plain <hostdev>

This is explained in excruciating detail in Patch 5, but in short, allowing the <teaming> element to be in a plain <hostdev> will permit someone who is running libvirt unprivileged, or in a container with no access to a PF device, to use the <teaming> feature, which encapsulates a lot of functionality related to assigning an SRIOV network device to a guest as a part of a failover bond device (the other device in the pair is an emulated virtio NIC), which in turn permits the guest to be migrated by transparently unplugging the SRIOV NIC prior to migration, and plugging a new one in after migration is completed. (Previously we required <interface type='hostdev'> for this feature, but that type of device needs to send netlink messages to the PF of the SRIOV VF that's being assigned, and that simply isn't possible sometimes.) Laine Stump (7): conf: make teaming info an official type conf: use virDomainNetTeamingInfoPtr instead of virDomainNetTeamingInfo conf: separate Parse/Format functions for virDomainNetTeamingInfo schema: separate teaming element definition from interface element conf: parse/format <teaming> element in plain <hostdev> qemu: plug <teaming> config from <hostdev> into qemu commandline news: document support for <teaming> in <hostdev> NEWS.rst | 6 ++ docs/formatdomain.rst | 51 +++++++++++ docs/schemas/domaincommon.rng | 42 +++++---- src/conf/domain_conf.c | 87 +++++++++++++------ src/conf/domain_conf.h | 13 ++- src/conf/domain_validate.c | 45 +++++++--- src/conf/virconftypes.h | 3 + src/libvirt_private.syms | 1 + src/qemu/qemu_command.c | 17 ++-- src/qemu/qemu_domain.c | 2 +- src/qemu/qemu_migration.c | 4 +- src/qemu/qemu_validate.c | 42 ++++----- .../net-virtio-teaming-hostdev.args | 40 +++++++++ .../net-virtio-teaming-hostdev.xml | 48 ++++++++++ tests/qemuxml2argvtest.c | 3 + .../net-virtio-teaming-hostdev.xml | 64 ++++++++++++++ tests/qemuxml2xmltest.c | 3 + 17 files changed, 384 insertions(+), 87 deletions(-) create mode 100644 tests/qemuxml2argvdata/net-virtio-teaming-hostdev.args create mode 100644 tests/qemuxml2argvdata/net-virtio-teaming-hostdev.xml create mode 100644 tests/qemuxml2xmloutdata/net-virtio-teaming-hostdev.xml -- 2.29.2

This struct was previously defined only within virDomainNetDef where it was used, but I need to also use it in virDomainHostdevDef, so move the internal struct out to its own "official" struct and give it the standard typedef duo and *Free() function. Signed-off-by: Laine Stump <laine@redhat.com> --- src/conf/domain_conf.c | 11 +++++++++++ src/conf/domain_conf.h | 12 ++++++++---- src/conf/virconftypes.h | 3 +++ src/libvirt_private.syms | 1 + 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 8f2207bdf6..7d7acb940a 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -2476,6 +2476,17 @@ virDomainVsockDefFree(virDomainVsockDefPtr vsock) } +void +virDomainNetTeamingInfoFree(virDomainNetTeamingInfoPtr teaming) +{ + if (!teaming) + return; + + g_free(teaming->persistent); + g_free(teaming); +} + + void virDomainNetDefFree(virDomainNetDefPtr def) { diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 8b1c8643be..92fe588b3f 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -956,6 +956,11 @@ typedef enum { VIR_DOMAIN_NET_TEAMING_TYPE_LAST } virDomainNetTeamingType; +struct _virDomainNetTeamingInfo { + virDomainNetTeamingType type; + char *persistent; /* alias name of persistent device */ +}; + /* link interface states */ typedef enum { VIR_DOMAIN_NET_INTERFACE_LINK_STATE_DEFAULT = 0, /* Default link state (up) */ @@ -1033,10 +1038,7 @@ struct _virDomainNetDef { char *tap; char *vhost; } backend; - struct { - virDomainNetTeamingType type; - char *persistent; /* alias name of persistent device */ - } teaming; + virDomainNetTeamingInfo teaming; union { virDomainChrSourceDefPtr vhostuser; struct { @@ -3100,6 +3102,8 @@ void virDomainActualNetDefFree(virDomainActualNetDefPtr def); virDomainVsockDefPtr virDomainVsockDefNew(virDomainXMLOptionPtr xmlopt); void virDomainVsockDefFree(virDomainVsockDefPtr vsock); G_DEFINE_AUTOPTR_CLEANUP_FUNC(virDomainVsockDef, virDomainVsockDefFree); +void virDomainNetTeamingInfoFree(virDomainNetTeamingInfoPtr teaming); +G_DEFINE_AUTOPTR_CLEANUP_FUNC(virDomainNetTeamingInfo, virDomainNetTeamingInfoFree); void virDomainNetDefFree(virDomainNetDefPtr def); G_DEFINE_AUTOPTR_CLEANUP_FUNC(virDomainNetDef, virDomainNetDefFree); void virDomainSmartcardDefFree(virDomainSmartcardDefPtr def); diff --git a/src/conf/virconftypes.h b/src/conf/virconftypes.h index 9042a2b34f..c51241cfa5 100644 --- a/src/conf/virconftypes.h +++ b/src/conf/virconftypes.h @@ -246,6 +246,9 @@ typedef virDomainNVRAMDef *virDomainNVRAMDefPtr; typedef struct _virDomainNetDef virDomainNetDef; typedef virDomainNetDef *virDomainNetDefPtr; +typedef struct _virDomainNetTeamingInfo virDomainNetTeamingInfo; +typedef virDomainNetTeamingInfo *virDomainNetTeamingInfoPtr; + typedef struct _virDomainOSDef virDomainOSDef; typedef virDomainOSDef *virDomainOSDefPtr; diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 837986845f..affa2df323 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -537,6 +537,7 @@ virDomainNetRemove; virDomainNetRemoveHostdev; virDomainNetResolveActualType; virDomainNetSetModelString; +virDomainNetTeamingInfoFree; virDomainNetTypeFromString; virDomainNetTypeSharesHostView; virDomainNetTypeToString; -- 2.29.2

To make it easier to split out the parsing/formatting of the <teaming> element into separate functions (so we can more easily add the <teaming> element to <hostdev>, change its virDomainNetDef so that it points to a virDomainNetTeamingInfo rather than containing one. Signed-off-by: Laine Stump <laine@redhat.com> --- src/conf/domain_conf.c | 31 ++++++++++++++++------------ src/conf/domain_conf.h | 2 +- src/conf/domain_validate.c | 26 ++++++++++++----------- src/qemu/qemu_command.c | 10 ++++----- src/qemu/qemu_domain.c | 2 +- src/qemu/qemu_migration.c | 4 ++-- src/qemu/qemu_validate.c | 42 ++++++++++++++++++++------------------ 7 files changed, 63 insertions(+), 54 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 7d7acb940a..bbf54c90ba 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -2542,7 +2542,7 @@ virDomainNetDefFree(virDomainNetDefPtr def) g_free(def->backend.tap); g_free(def->backend.vhost); - g_free(def->teaming.persistent); + virDomainNetTeamingInfoFree(def->teaming); g_free(def->virtPortProfile); g_free(def->script); g_free(def->downscript); @@ -11447,18 +11447,23 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, } } - if (teamingType) { - int tmpTeaming; + if (teamingType || teamingPersistent) { + def->teaming = g_new0(virDomainNetTeamingInfo, 1); - if ((tmpTeaming = virDomainNetTeamingTypeFromString(teamingType)) <= 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unknown teaming type '%s'"), - teamingType); - goto error; + if (teamingType) { + int tmpTeaming; + + if ((tmpTeaming = virDomainNetTeamingTypeFromString(teamingType)) <= 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown teaming type '%s'"), + teamingType); + goto error; + } + def->teaming->type = tmpTeaming; } - def->teaming.type = tmpTeaming; + + def->teaming->persistent = g_steal_pointer(&teamingPersistent); } - def->teaming.persistent = g_steal_pointer(&teamingPersistent); rv = virXPathULong("string(./tune/sndbuf)", ctxt, &def->tune.sndbuf); if (rv >= 0) { @@ -25825,10 +25830,10 @@ virDomainNetDefFormat(virBufferPtr buf, virBufferAddLit(buf, "</tune>\n"); } - if (def->teaming.type != VIR_DOMAIN_NET_TEAMING_TYPE_NONE) { + if (def->teaming && def->teaming->type != VIR_DOMAIN_NET_TEAMING_TYPE_NONE) { virBufferAsprintf(buf, "<teaming type='%s'", - virDomainNetTeamingTypeToString(def->teaming.type)); - virBufferEscapeString(buf, " persistent='%s'", def->teaming.persistent); + virDomainNetTeamingTypeToString(def->teaming->type)); + virBufferEscapeString(buf, " persistent='%s'", def->teaming->persistent); virBufferAddLit(buf, "/>\n"); } if (def->linkstate) { diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 92fe588b3f..fb695a212b 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1038,7 +1038,7 @@ struct _virDomainNetDef { char *tap; char *vhost; } backend; - virDomainNetTeamingInfo teaming; + virDomainNetTeamingInfoPtr teaming; union { virDomainChrSourceDefPtr vhostuser; struct { diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c index b6f53886cd..703946b3e5 100644 --- a/src/conf/domain_validate.c +++ b/src/conf/domain_validate.c @@ -1507,18 +1507,20 @@ virDomainNetDefValidate(const virDomainNetDef *net) return -1; } - if (net->teaming.type == VIR_DOMAIN_NET_TEAMING_TYPE_TRANSIENT) { - if (!net->teaming.persistent) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("teaming persistent attribute must be set if teaming type is 'transient'")); - return -1; - } - } else { - if (net->teaming.persistent) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("teaming persistent attribute not allowed if teaming type is '%s'"), - virDomainNetTeamingTypeToString(net->teaming.type)); - return -1; + if (net->teaming) { + if (net->teaming->type == VIR_DOMAIN_NET_TEAMING_TYPE_TRANSIENT) { + if (!net->teaming->persistent) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("teaming persistent attribute must be set if teaming type is 'transient'")); + return -1; + } + } else { + if (net->teaming->persistent) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("teaming persistent attribute not allowed if teaming type is '%s'"), + virDomainNetTeamingTypeToString(net->teaming->type)); + return -1; + } } } diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 6b6e0bfc34..09516d407f 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -3536,7 +3536,7 @@ qemuBuildNicDevStr(virDomainDefPtr def, if (net->mtu) virBufferAsprintf(&buf, ",host_mtu=%u", net->mtu); - if (net->teaming.type == VIR_DOMAIN_NET_TEAMING_TYPE_PERSISTENT) + if (net->teaming && net->teaming->type == VIR_DOMAIN_NET_TEAMING_TYPE_PERSISTENT) virBufferAddLit(&buf, ",failover=on"); } @@ -4461,11 +4461,11 @@ qemuBuildPCIHostdevDevStr(const virDomainDef *def, if (qemuBuildRomStr(&buf, dev->info) < 0) return NULL; - if (dev->parentnet && - dev->parentnet->teaming.type == VIR_DOMAIN_NET_TEAMING_TYPE_TRANSIENT && - dev->parentnet->teaming.persistent) { + if (dev->parentnet && dev->parentnet->teaming && + dev->parentnet->teaming->type == VIR_DOMAIN_NET_TEAMING_TYPE_TRANSIENT && + dev->parentnet->teaming->persistent) { virBufferAsprintf(&buf, ",failover_pair_id=%s", - dev->parentnet->teaming.persistent); + dev->parentnet->teaming->persistent); } return virBufferContentAndReset(&buf); diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 53b4fb5f4f..2adf3d2f37 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -4458,7 +4458,7 @@ qemuDomainValidateActualNetDef(const virDomainNetDef *net, return -1; } - if (net->teaming.type == VIR_DOMAIN_NET_TEAMING_TYPE_TRANSIENT && + if (net->teaming && net->teaming->type == VIR_DOMAIN_NET_TEAMING_TYPE_TRANSIENT && actualType != VIR_DOMAIN_NET_TYPE_HOSTDEV) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("interface %s - teaming transient device must be type='hostdev', not '%s'"), diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index f44d31c971..5d794ab4d0 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -1223,8 +1223,8 @@ qemuMigrationSrcIsAllowedHostdev(const virDomainDef *def) * the device will be auto-unplugged by QEMU during * migration. */ - if (hostdev->parentnet && - hostdev->parentnet->teaming.type == VIR_DOMAIN_NET_TEAMING_TYPE_TRANSIENT) { + if (hostdev->parentnet && hostdev->parentnet->teaming && + hostdev->parentnet->teaming->type == VIR_DOMAIN_NET_TEAMING_TYPE_TRANSIENT) { continue; } diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index a70737327e..45745de289 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -1588,26 +1588,28 @@ qemuValidateDomainDeviceDefNetwork(const virDomainNetDef *net, return -1; } - if (net->teaming.type != VIR_DOMAIN_NET_TEAMING_TYPE_NONE && - !virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_NET_FAILOVER)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("virtio-net failover (teaming) is not supported with this QEMU binary")); - return -1; - } - if (net->teaming.type == VIR_DOMAIN_NET_TEAMING_TYPE_PERSISTENT - && !virDomainNetIsVirtioModel(net)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("virtio-net teaming persistent interface must be <model type='virtio'/>, not '%s'"), - virDomainNetGetModelString(net)); - return -1; - } - if (net->teaming.type == VIR_DOMAIN_NET_TEAMING_TYPE_TRANSIENT && - net->type != VIR_DOMAIN_NET_TYPE_HOSTDEV && - net->type != VIR_DOMAIN_NET_TYPE_NETWORK) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("virtio-net teaming transient interface must be type='hostdev', not '%s'"), - virDomainNetTypeToString(net->type)); - return -1; + if (net->teaming) { + if (net->teaming->type != VIR_DOMAIN_NET_TEAMING_TYPE_NONE && + !virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_NET_FAILOVER)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("virtio-net failover (teaming) is not supported with this QEMU binary")); + return -1; + } + if (net->teaming->type == VIR_DOMAIN_NET_TEAMING_TYPE_PERSISTENT + && !virDomainNetIsVirtioModel(net)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("virtio-net teaming persistent interface must be <model type='virtio'/>, not '%s'"), + virDomainNetGetModelString(net)); + return -1; + } + if (net->teaming->type == VIR_DOMAIN_NET_TEAMING_TYPE_TRANSIENT && + net->type != VIR_DOMAIN_NET_TYPE_HOSTDEV && + net->type != VIR_DOMAIN_NET_TYPE_NETWORK) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("virtio-net teaming transient interface must be type='hostdev', not '%s'"), + virDomainNetTypeToString(net->type)); + return -1; + } } if (net->coalesce && !qemuValidateNetSupportsCoalesce(net->type)) { -- 2.29.2

In preparation for using the same element in two places, split the parsing/formating for that subelement out of the virDomainNetDef functions into their own functions. Signed-off-by: Laine Stump <laine@redhat.com> --- src/conf/domain_conf.c | 74 +++++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 29 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index bbf54c90ba..3fe8517f39 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -10629,6 +10629,34 @@ virDomainChrSourceReconnectDefParseXML(virDomainChrSourceReconnectDefPtr def, } +static int +virDomainNetTeamingInfoParseXML(xmlXPathContextPtr ctxt, + virDomainNetTeamingInfoPtr *teaming) +{ + g_autofree char *typeStr = virXPathString("string(./teaming/@type)", ctxt); + g_autofree char *persistentStr = virXPathString("string(./teaming/@persistent)", ctxt); + g_autoptr(virDomainNetTeamingInfo) tmpTeaming = NULL; + int tmpType; + + if (!typeStr && !persistentStr) + return 0; + + tmpTeaming = g_new0(virDomainNetTeamingInfo, 1); + + if ((tmpType = virDomainNetTeamingTypeFromString(typeStr)) <= 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown teaming type '%s'"), + typeStr); + return -1; + } + + tmpTeaming->type = tmpType; + tmpTeaming->persistent = g_steal_pointer(&persistentStr); + *teaming = g_steal_pointer(&tmpTeaming); + return 0; +} + + static virDomainNetDefPtr virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, xmlNodePtr node, @@ -10683,8 +10711,6 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, g_autofree char *vhostuser_type = NULL; g_autofree char *trustGuestRxFilters = NULL; g_autofree char *vhost_path = NULL; - g_autofree char *teamingType = NULL; - g_autofree char *teamingPersistent = NULL; const char *prefix = xmlopt ? xmlopt->config.netPrefix : NULL; if (!(def = virDomainNetDefNew(xmlopt))) @@ -10895,10 +10921,6 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, if (!vhost_path && (tmp = virXMLPropString(cur, "vhost"))) vhost_path = virFileSanitizePath(tmp); VIR_FREE(tmp); - } else if (virXMLNodeNameEqual(cur, "teaming") && - !teamingType && !teamingPersistent) { - teamingType = virXMLPropString(cur, "type"); - teamingPersistent = virXMLPropString(cur, "persistent"); } } cur = cur->next; @@ -11447,23 +11469,8 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, } } - if (teamingType || teamingPersistent) { - def->teaming = g_new0(virDomainNetTeamingInfo, 1); - - if (teamingType) { - int tmpTeaming; - - if ((tmpTeaming = virDomainNetTeamingTypeFromString(teamingType)) <= 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unknown teaming type '%s'"), - teamingType); - goto error; - } - def->teaming->type = tmpTeaming; - } - - def->teaming->persistent = g_steal_pointer(&teamingPersistent); - } + if (virDomainNetTeamingInfoParseXML(ctxt, &def->teaming) < 0) + goto error; rv = virXPathULong("string(./tune/sndbuf)", ctxt, &def->tune.sndbuf); if (rv >= 0) { @@ -25513,6 +25520,19 @@ virDomainChrSourceReconnectDefFormat(virBufferPtr buf, } +static void +virDomainNetTeamingInfoFormat(virDomainNetTeamingInfoPtr teaming, + virBufferPtr buf) +{ + if (teaming && teaming->type != VIR_DOMAIN_NET_TEAMING_TYPE_NONE) { + virBufferAsprintf(buf, "<teaming type='%s'", + virDomainNetTeamingTypeToString(teaming->type)); + virBufferEscapeString(buf, " persistent='%s'", teaming->persistent); + virBufferAddLit(buf, "/>\n"); + } +} + + int virDomainNetDefFormat(virBufferPtr buf, virDomainNetDefPtr def, @@ -25830,12 +25850,8 @@ virDomainNetDefFormat(virBufferPtr buf, virBufferAddLit(buf, "</tune>\n"); } - if (def->teaming && def->teaming->type != VIR_DOMAIN_NET_TEAMING_TYPE_NONE) { - virBufferAsprintf(buf, "<teaming type='%s'", - virDomainNetTeamingTypeToString(def->teaming->type)); - virBufferEscapeString(buf, " persistent='%s'", def->teaming->persistent); - virBufferAddLit(buf, "/>\n"); - } + virDomainNetTeamingInfoFormat(def->teaming, buf); + if (def->linkstate) { virBufferAsprintf(buf, "<link state='%s'/>\n", virDomainNetInterfaceLinkStateTypeToString(def->linkstate)); -- 2.29.2

Signed-off-by: Laine Stump <laine@redhat.com> --- docs/schemas/domaincommon.rng | 39 ++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 7a2706a4fb..31960fb7cf 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -3529,23 +3529,7 @@ <ref name="portOptions"/> </optional> <optional> - <element name="teaming"> - <choice> - <group> - <attribute name="type"> - <value>persistent</value> - </attribute> - </group> - <group> - <attribute name="type"> - <value>transient</value> - </attribute> - <attribute name="persistent"> - <ref name="aliasName"/> - </attribute> - </group> - </choice> - </element> + <ref name="teaming"/> </optional> </interleave> </define> @@ -3581,6 +3565,27 @@ <ref name="route"/> </zeroOrMore> </define> + + <define name="teaming"> + <element name="teaming"> + <choice> + <group> + <attribute name="type"> + <value>persistent</value> + </attribute> + </group> + <group> + <attribute name="type"> + <value>transient</value> + </attribute> + <attribute name="persistent"> + <ref name="aliasName"/> + </attribute> + </group> + </choice> + </element> + </define> + <!-- An emulator description is just a path to the binary used for the task --> -- 2.29.2

The <teaming> element in <interface> allows pairing two interfaces together as a simple "failover bond" network device in a guest. One of the devices will be the "transient" interface - it will be preferred for all network traffic when it is present, but may be removed when necessary, in particular during migration, when traffic will instead go through the other interface of the pair - the "persistent" interface. As it happens, in the QEMU implementation of this teaming pair (called "virtio failover" in QEMU) the transient interface is always a host network device assigned to the guest using VFIO (aka "hostdev"); the persistent interface is always an emulated virtio NIC. When support was initially added for <teaming>, it was written to require that the transient/hostdev device be defined using <interface type='hostdev'>; this was done because the virtio failover implementation in QEMU and the virtio guest driver demands that the two interfaces in the pair have matching MAC addresses, and the only way libvirt can guarantee the MAC address of a hostdev network device is to use <interface type='hostdev'>, whose main purpose is to configure the device's MAC address. (note that <interface type='hostdev'> in turn requires that the network device be an SRIOV VF (Virtual Function), as that is the only type of network device whose MAC address we can set in a way that will survive the device's driver init in the guest). It has recently come up that some users are unable to use <teaming> because they are running in a container environment where libvirt doesn't have the necessary privileges or resources to set the VF's MAC address (because setting the VF MAC is done via the same device's PF (Physical Function), and the PF is not exposed to libvirt's container. At the same time, they *are* able to set the VF's MAC address in advance of staring up libvirt in the container. So they could theoretically use the <teaming> feature if libvirt just skipped the "setting the MAC address" part. Fortunately, that is *exactly* the difference between <interface type='hostdev'> (a "hostdev VF") and <hostdev> (a "plain hostdev" - it could be *any PCI device; libvirt doesn't know what type of PCI device it is, and doesn't care). But what *is* still needed is for libvirt to provide a small bit of information on the commandline argument for the hostdev, telling QEMU that this device will be part of a team ("failover pair"), and the id of the other device in the pair. So, what we need to do is add support for the <teaming> element to plain <hostdev>, and that is what this patch does. (actually, this patch adds parsing/formatting of the <teaming> element in <hostdev>. The next patch will actually wire that into the qemu driver.) Signed-off-by: Laine Stump <laine@redhat.com> --- docs/formatdomain.rst | 51 +++++++++++++++ docs/schemas/domaincommon.rng | 3 + src/conf/domain_conf.c | 5 ++ src/conf/domain_conf.h | 1 + src/conf/domain_validate.c | 19 ++++++ .../net-virtio-teaming-hostdev.xml | 48 ++++++++++++++ .../net-virtio-teaming-hostdev.xml | 64 +++++++++++++++++++ tests/qemuxml2xmltest.c | 3 + 8 files changed, 194 insertions(+) create mode 100644 tests/qemuxml2argvdata/net-virtio-teaming-hostdev.xml create mode 100644 tests/qemuxml2xmloutdata/net-virtio-teaming-hostdev.xml diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 2493be595f..eafd6b3396 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -4837,6 +4837,22 @@ support in the hypervisor and the guest network driver). </devices> ... +The second interface in this example is referencing a network that is +a pool of SRIOV VFs (i.e. a "hostdev network"). You could instead +directly reference an SRIOV VF device: + +:: + + ... + <interface type='hostdev'> + <source> + <address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/> + </source> + <mac address='00:11:22:33:44:55:66'/> + <teaming type='transient' persistent='ua-backup0'/> + </interface> + ... + The ``<teaming>`` element required attribute ``type`` will be set to either ``"persistent"`` to indicate a device that should always be present in the domain, or ``"transient"`` to indicate a device that may periodically be @@ -4858,6 +4874,41 @@ once migration is completed; while migration is taking place, network traffic will use the virtio NIC. (Of course the emulated virtio NIC and the hostdev NIC must be connected to the same subnet for bonding to work properly). +:since:`Since 7.1.0` The ``<teaming>`` element can also be added to a +plain ``<hostdev>`` device. + +:: + + ... + <hostdev mode='subsystem' type='pci' managed='no'> + <source> + <address domain='0x0000' bus='0x00' slot='0x07' function='0x0'/> + </source> + <mac address='00:11:22:33:44:55:66'/> + <teaming type='transient' persistent='ua-backup0'/> + </interface> + ... + +This device must be a network device, but not necessarily an SRIOV +VF. Using plain ``<hostdev>`` rather than ``<interface +type='hostdev'>`` or ``<interface type='network'>`` is useful if the +device that will be assigned with VFIO is a standard NIC (not a VF) or +if libvirt doesn't have the necessary resources and privileges to set +the VF's MAC address (e.g. if libvirt is running unprivileged, or in a +container). This of course means that the user (or another +application) is responsible for setting the MAC address of the device +in a way such that it will survive guest driver initialization. For +standard NICs (i.e. not an SRIOV VF) this probably means that the +NIC's factory-programmed MAC address will need to be used for the +teaming pair (since any driver init in the guest will reset the MAC +back to factory). If it is an SRIOV VF, then its MAC address will need +to be set via the VF's PF, e.g. if you are going to use VF 2 of the PF +enp2s0f1, you would use something like this command: + +:: + + ip link set enp2s0f1 vf 2 mac 52:54:00:11:22:33 + NB1: Since you must know the alias name of the virtio NIC when configuring the hostdev NIC, it will need to be manually set in the virtio NIC's configuration (as with all other manually set alias names, this means it must start with diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 31960fb7cf..e6de934456 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -5156,6 +5156,9 @@ <empty/> </element> </optional> + <optional> + <ref name="teaming"/> + </optional> <element name="source"> <optional> <ref name="startupPolicy"/> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 3fe8517f39..8701136aa9 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -15015,6 +15015,9 @@ virDomainHostdevDefParseXML(virDomainXMLOptionPtr xmlopt, } } + if (virDomainNetTeamingInfoParseXML(ctxt, &def->teaming) < 0) + goto error; + return def; error: @@ -27433,6 +27436,8 @@ virDomainHostdevDefFormat(virBufferPtr buf, break; } + virDomainNetTeamingInfoFormat(def->teaming, buf); + if (def->readonly) virBufferAddLit(buf, "<readonly/>\n"); if (def->shareable) diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index fb695a212b..e263e6098b 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -354,6 +354,7 @@ struct _virDomainHostdevDef { virDomainHostdevCaps caps; } source; virDomainHostdevOrigStates origstates; + virDomainNetTeamingInfoPtr teaming; virDomainDeviceInfoPtr info; /* Guest address */ }; diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c index 703946b3e5..b47ecba86b 100644 --- a/src/conf/domain_validate.c +++ b/src/conf/domain_validate.c @@ -1585,6 +1585,25 @@ virDomainHostdevDefValidate(const virDomainHostdevDef *hostdev) break; } } + + if (hostdev->teaming) { + if (hostdev->teaming->type != VIR_DOMAIN_NET_TEAMING_TYPE_TRANSIENT) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("teaming hostdev devices must have type='transient'")); + return -1; + } + if (!hostdev->teaming->persistent) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("missing required persistent attribute in hostdev teaming element")); + return -1; + } + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || + hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("teaming is only supported for pci hostdev devices")); + return -1; + } + } return 0; } diff --git a/tests/qemuxml2argvdata/net-virtio-teaming-hostdev.xml b/tests/qemuxml2argvdata/net-virtio-teaming-hostdev.xml new file mode 100644 index 0000000000..b176e66a00 --- /dev/null +++ b/tests/qemuxml2argvdata/net-virtio-teaming-hostdev.xml @@ -0,0 +1,48 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-system-i386</emulator> + <disk type='block' device='disk'> + <source dev='/dev/HostVG/QEMUGuest1'/> + <target dev='hda' bus='ide'/> + </disk> + <controller type='usb' index='0'/> + <interface type='user'> + <mac address='00:11:22:33:44:55'/> + <model type='virtio'/> + <teaming type='persistent'/> + <alias name='ua-backup0'/> + </interface> + <interface type='user'> + <mac address='66:44:33:22:11:00'/> + <model type='virtio'/> + <teaming type='persistent'/> + <alias name='ua-backup1'/> + </interface> + <hostdev mode='subsystem' type='pci' managed='yes'> + <source> + <address domain='0x0000' bus='0x03' slot='0x07' function='0x1'/> + </source> + <teaming type='transient' persistent='ua-backup0'/> + </hostdev> + <hostdev mode='subsystem' type='pci' managed='yes'> + <source> + <address domain='0x0000' bus='0x03' slot='0x07' function='0x2'/> + </source> + <teaming type='transient' persistent='ua-backup1'/> + </hostdev> + <memballoon model='virtio'/> + </devices> +</domain> diff --git a/tests/qemuxml2xmloutdata/net-virtio-teaming-hostdev.xml b/tests/qemuxml2xmloutdata/net-virtio-teaming-hostdev.xml new file mode 100644 index 0000000000..a7edab63bd --- /dev/null +++ b/tests/qemuxml2xmloutdata/net-virtio-teaming-hostdev.xml @@ -0,0 +1,64 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-system-i386</emulator> + <disk type='block' device='disk'> + <driver name='qemu' type='raw'/> + <source dev='/dev/HostVG/QEMUGuest1'/> + <target dev='hda' bus='ide'/> + <address type='drive' controller='0' bus='0' target='0' unit='0'/> + </disk> + <controller type='usb' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <controller type='ide' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/> + </controller> + <interface type='user'> + <mac address='00:11:22:33:44:55'/> + <model type='virtio'/> + <teaming type='persistent'/> + <alias name='ua-backup0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + </interface> + <interface type='user'> + <mac address='66:44:33:22:11:00'/> + <model type='virtio'/> + <teaming type='persistent'/> + <alias name='ua-backup1'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> + </interface> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <hostdev mode='subsystem' type='pci' managed='yes'> + <source> + <address domain='0x0000' bus='0x03' slot='0x07' function='0x1'/> + </source> + <teaming type='transient' persistent='ua-backup0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/> + </hostdev> + <hostdev mode='subsystem' type='pci' managed='yes'> + <source> + <address domain='0x0000' bus='0x03' slot='0x07' function='0x2'/> + </source> + <teaming type='transient' persistent='ua-backup1'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/> + </hostdev> + <memballoon model='virtio'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/> + </memballoon> + </devices> +</domain> diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index a00ebd7d76..5cd945f28f 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -438,6 +438,9 @@ mymain(void) DO_TEST("net-virtio-teaming-network", QEMU_CAPS_VIRTIO_NET_FAILOVER, QEMU_CAPS_DEVICE_VFIO_PCI); + DO_TEST("net-virtio-teaming-hostdev", + QEMU_CAPS_VIRTIO_NET_FAILOVER, + QEMU_CAPS_DEVICE_VFIO_PCI); DO_TEST_CAPS_LATEST("net-isolated-port"); DO_TEST("net-hostdev", NONE); DO_TEST("net-hostdev-bootorder", NONE); -- 2.29.2

On 2/11/21 8:57 AM, Laine Stump wrote:
The <teaming> element in <interface> allows pairing two interfaces together as a simple "failover bond" network device in a guest. One of the devices will be the "transient" interface - it will be preferred for all network traffic when it is present, but may be removed when necessary, in particular during migration, when traffic will instead go through the other interface of the pair - the "persistent" interface. As it happens, in the QEMU implementation of this teaming pair (called "virtio failover" in QEMU) the transient interface is always a host network device assigned to the guest using VFIO (aka "hostdev"); the persistent interface is always an emulated virtio NIC.
When support was initially added for <teaming>, it was written to require that the transient/hostdev device be defined using <interface type='hostdev'>; this was done because the virtio failover implementation in QEMU and the virtio guest driver demands that the two interfaces in the pair have matching MAC addresses, and the only way libvirt can guarantee the MAC address of a hostdev network device is to use <interface type='hostdev'>, whose main purpose is to configure the device's MAC address. (note that <interface type='hostdev'> in turn requires that the network device be an SRIOV VF (Virtual Function), as that is the only type of network device whose MAC address we can set in a way that will survive the device's driver init in the guest).
It has recently come up that some users are unable to use <teaming> because they are running in a container environment where libvirt doesn't have the necessary privileges or resources to set the VF's MAC address (because setting the VF MAC is done via the same device's PF (Physical Function), and the PF is not exposed to libvirt's container.
At the same time, they *are* able to set the VF's MAC address in advance of staring up libvirt in the container. So they could theoretically use the <teaming> feature if libvirt just skipped the "setting the MAC address" part.
Fortunately, that is *exactly* the difference between <interface type='hostdev'> (a "hostdev VF") and <hostdev> (a "plain hostdev" - it could be *any PCI device; libvirt doesn't know what type of PCI device it is, and doesn't care).
But what *is* still needed is for libvirt to provide a small bit of information on the commandline argument for the hostdev, telling QEMU that this device will be part of a team ("failover pair"), and the id of the other device in the pair.
So, what we need to do is add support for the <teaming> element to plain <hostdev>, and that is what this patch does.
(actually, this patch adds parsing/formatting of the <teaming> element in <hostdev>. The next patch will actually wire that into the qemu driver.)
Signed-off-by: Laine Stump <laine@redhat.com> --- docs/formatdomain.rst | 51 +++++++++++++++ docs/schemas/domaincommon.rng | 3 + src/conf/domain_conf.c | 5 ++ src/conf/domain_conf.h | 1 + src/conf/domain_validate.c | 19 ++++++ .../net-virtio-teaming-hostdev.xml | 48 ++++++++++++++ .../net-virtio-teaming-hostdev.xml | 64 +++++++++++++++++++ tests/qemuxml2xmltest.c | 3 + 8 files changed, 194 insertions(+) create mode 100644 tests/qemuxml2argvdata/net-virtio-teaming-hostdev.xml create mode 100644 tests/qemuxml2xmloutdata/net-virtio-teaming-hostdev.xml
There're only very few of differences between these two files (mostly PCI addresses in the out xml) and neither of them is related to this feature. Perhaps make one symlink of the other and add those diffs into the input xml?
diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 2493be595f..eafd6b3396 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -4837,6 +4837,22 @@ support in the hypervisor and the guest network driver). </devices> ...
+The second interface in this example is referencing a network that is +a pool of SRIOV VFs (i.e. a "hostdev network"). You could instead +directly reference an SRIOV VF device: + +:: + + ... + <interface type='hostdev'> + <source> + <address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/> + </source> + <mac address='00:11:22:33:44:55:66'/> + <teaming type='transient' persistent='ua-backup0'/> + </interface> + ... + The ``<teaming>`` element required attribute ``type`` will be set to either ``"persistent"`` to indicate a device that should always be present in the domain, or ``"transient"`` to indicate a device that may periodically be @@ -4858,6 +4874,41 @@ once migration is completed; while migration is taking place, network traffic will use the virtio NIC. (Of course the emulated virtio NIC and the hostdev NIC must be connected to the same subnet for bonding to work properly).
+:since:`Since 7.1.0` The ``<teaming>`` element can also be added to a +plain ``<hostdev>`` device. + +:: + + ... + <hostdev mode='subsystem' type='pci' managed='no'> + <source> + <address domain='0x0000' bus='0x00' slot='0x07' function='0x0'/> + </source> + <mac address='00:11:22:33:44:55:66'/> + <teaming type='transient' persistent='ua-backup0'/> + </interface> + ... + +This device must be a network device, but not necessarily an SRIOV +VF. Using plain ``<hostdev>`` rather than ``<interface +type='hostdev'>`` or ``<interface type='network'>`` is useful if the +device that will be assigned with VFIO is a standard NIC (not a VF) or +if libvirt doesn't have the necessary resources and privileges to set +the VF's MAC address (e.g. if libvirt is running unprivileged, or in a +container). This of course means that the user (or another +application) is responsible for setting the MAC address of the device +in a way such that it will survive guest driver initialization. For +standard NICs (i.e. not an SRIOV VF) this probably means that the +NIC's factory-programmed MAC address will need to be used for the +teaming pair (since any driver init in the guest will reset the MAC +back to factory). If it is an SRIOV VF, then its MAC address will need +to be set via the VF's PF, e.g. if you are going to use VF 2 of the PF +enp2s0f1, you would use something like this command: + +:: + + ip link set enp2s0f1 vf 2 mac 52:54:00:11:22:33 + NB1: Since you must know the alias name of the virtio NIC when configuring the hostdev NIC, it will need to be manually set in the virtio NIC's configuration (as with all other manually set alias names, this means it must start with diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 31960fb7cf..e6de934456 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -5156,6 +5156,9 @@ <empty/> </element> </optional> + <optional> + <ref name="teaming"/> + </optional> <element name="source"> <optional> <ref name="startupPolicy"/> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 3fe8517f39..8701136aa9 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -15015,6 +15015,9 @@ virDomainHostdevDefParseXML(virDomainXMLOptionPtr xmlopt, } }
+ if (virDomainNetTeamingInfoParseXML(ctxt, &def->teaming) < 0) + goto error; + return def;
error:
This should be paired with freeing the teaming info: diff --git i/src/conf/domain_conf.c w/src/conf/domain_conf.c index 3208e89c65..1d2a507fa1 100644 --- i/src/conf/domain_conf.c +++ w/src/conf/domain_conf.c @@ -3024,6 +3024,8 @@ void virDomainHostdevDefClear(virDomainHostdevDefPtr def) if (!def->parentnet) virDomainDeviceInfoFree(def->info); + virDomainNetTeamingInfoFree(def->teaming); + switch (def->mode) { case VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES: switch ((virDomainHostdevCapsType) def->source.caps.type) { Michal

Signed-off-by: Laine Stump <laine@redhat.com> --- src/qemu/qemu_command.c | 15 ++++--- .../net-virtio-teaming-hostdev.args | 40 +++++++++++++++++++ tests/qemuxml2argvtest.c | 3 ++ 3 files changed, 53 insertions(+), 5 deletions(-) create mode 100644 tests/qemuxml2argvdata/net-virtio-teaming-hostdev.args diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 09516d407f..30663c933e 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -4427,6 +4427,7 @@ qemuBuildPCIHostdevDevStr(const virDomainDef *def, g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; virDomainHostdevSubsysPCIPtr pcisrc = &dev->source.subsys.u.pci; int backend = pcisrc->backend; + virDomainNetTeamingInfoPtr teaming; /* caller has to assign proper passthrough backend type */ switch ((virDomainHostdevSubsysPCIBackendType)backend) { @@ -4461,11 +4462,15 @@ qemuBuildPCIHostdevDevStr(const virDomainDef *def, if (qemuBuildRomStr(&buf, dev->info) < 0) return NULL; - if (dev->parentnet && dev->parentnet->teaming && - dev->parentnet->teaming->type == VIR_DOMAIN_NET_TEAMING_TYPE_TRANSIENT && - dev->parentnet->teaming->persistent) { - virBufferAsprintf(&buf, ",failover_pair_id=%s", - dev->parentnet->teaming->persistent); + if (dev->parentnet) + teaming = dev->parentnet->teaming; + else + teaming = dev->teaming; + + if (teaming && + teaming->type == VIR_DOMAIN_NET_TEAMING_TYPE_TRANSIENT && + teaming->persistent) { + virBufferAsprintf(&buf, ",failover_pair_id=%s", teaming->persistent); } return virBufferContentAndReset(&buf); diff --git a/tests/qemuxml2argvdata/net-virtio-teaming-hostdev.args b/tests/qemuxml2argvdata/net-virtio-teaming-hostdev.args new file mode 100644 index 0000000000..19e7260843 --- /dev/null +++ b/tests/qemuxml2argvdata/net-virtio-teaming-hostdev.args @@ -0,0 +1,40 @@ +LC_ALL=C \ +PATH=/bin \ +HOME=/tmp/lib/domain--1-QEMUGuest1 \ +USER=test \ +LOGNAME=test \ +XDG_DATA_HOME=/tmp/lib/domain--1-QEMUGuest1/.local/share \ +XDG_CACHE_HOME=/tmp/lib/domain--1-QEMUGuest1/.cache \ +XDG_CONFIG_HOME=/tmp/lib/domain--1-QEMUGuest1/.config \ +QEMU_AUDIO_DRV=none \ +/usr/bin/qemu-system-i386 \ +-name QEMUGuest1 \ +-S \ +-machine pc,accel=tcg,usb=off,dump-guest-core=off \ +-m 214 \ +-realtime mlock=off \ +-smp 1,sockets=1,cores=1,threads=1 \ +-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \ +-display none \ +-no-user-config \ +-nodefaults \ +-chardev socket,id=charmonitor,path=/tmp/lib/domain--1-QEMUGuest1/monitor.sock,\ +server,nowait \ +-mon chardev=charmonitor,id=monitor,mode=control \ +-rtc base=utc \ +-no-shutdown \ +-no-acpi \ +-usb \ +-drive file=/dev/HostVG/QEMUGuest1,format=raw,if=none,id=drive-ide0-0-0 \ +-device ide-hd,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0,bootindex=1 \ +-netdev user,id=hostua-backup0 \ +-device virtio-net-pci,failover=on,netdev=hostua-backup0,id=ua-backup0,\ +mac=00:11:22:33:44:55,bus=pci.0,addr=0x3 \ +-netdev user,id=hostua-backup1 \ +-device virtio-net-pci,failover=on,netdev=hostua-backup1,id=ua-backup1,\ +mac=66:44:33:22:11:00,bus=pci.0,addr=0x4 \ +-device vfio-pci,host=0000:03:07.1,id=hostdev0,bus=pci.0,addr=0x5,\ +failover_pair_id=ua-backup0 \ +-device vfio-pci,host=0000:03:07.2,id=hostdev1,bus=pci.0,addr=0x6,\ +failover_pair_id=ua-backup1 \ +-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x7 diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index faa71a7a16..93d6a608bf 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -1503,6 +1503,9 @@ mymain(void) QEMU_CAPS_VIRTIO_NET_FAILOVER, QEMU_CAPS_DEVICE_VFIO_PCI); DO_TEST_PARSE_ERROR("net-virtio-teaming", NONE); + DO_TEST("net-virtio-teaming-hostdev", + QEMU_CAPS_VIRTIO_NET_FAILOVER, + QEMU_CAPS_DEVICE_VFIO_PCI); DO_TEST("net-eth", NONE); DO_TEST("net-eth-ifname", NONE); DO_TEST("net-eth-names", NONE); -- 2.29.2

Signed-off-by: Laine Stump <laine@redhat.com> --- NEWS.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/NEWS.rst b/NEWS.rst index c6ae6a6c60..a9a1a61119 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -35,6 +35,12 @@ v7.1.0 (unreleased) ``virNetworkGetXMLDesc()``, and ``virDomainScreenshot()``, APIs have been implemented in the Hyper-V driver. + * Support <teaming> element in plain <hostdev> devices + + This is useful when libvirt doesn't have the privileges necessary + to set the hostdev device's MAC address (which is a necessary + part of the alternate <interface type='hostdev'>). + * **Improvements** * **Bug fixes** -- 2.29.2

On 2/11/21 8:57 AM, Laine Stump wrote:
This is explained in excruciating detail in Patch 5, but in short, allowing the <teaming> element to be in a plain <hostdev> will permit someone who is running libvirt unprivileged, or in a container with no access to a PF device, to use the <teaming> feature, which encapsulates a lot of functionality related to assigning an SRIOV network device to a guest as a part of a failover bond device (the other device in the pair is an emulated virtio NIC), which in turn permits the guest to be migrated by transparently unplugging the SRIOV NIC prior to migration, and plugging a new one in after migration is completed.
(Previously we required <interface type='hostdev'> for this feature, but that type of device needs to send netlink messages to the PF of the SRIOV VF that's being assigned, and that simply isn't possible sometimes.)
Laine Stump (7): conf: make teaming info an official type conf: use virDomainNetTeamingInfoPtr instead of virDomainNetTeamingInfo conf: separate Parse/Format functions for virDomainNetTeamingInfo schema: separate teaming element definition from interface element conf: parse/format <teaming> element in plain <hostdev> qemu: plug <teaming> config from <hostdev> into qemu commandline news: document support for <teaming> in <hostdev>
NEWS.rst | 6 ++ docs/formatdomain.rst | 51 +++++++++++ docs/schemas/domaincommon.rng | 42 +++++---- src/conf/domain_conf.c | 87 +++++++++++++------ src/conf/domain_conf.h | 13 ++- src/conf/domain_validate.c | 45 +++++++--- src/conf/virconftypes.h | 3 + src/libvirt_private.syms | 1 + src/qemu/qemu_command.c | 17 ++-- src/qemu/qemu_domain.c | 2 +- src/qemu/qemu_migration.c | 4 +- src/qemu/qemu_validate.c | 42 ++++----- .../net-virtio-teaming-hostdev.args | 40 +++++++++ .../net-virtio-teaming-hostdev.xml | 48 ++++++++++ tests/qemuxml2argvtest.c | 3 + .../net-virtio-teaming-hostdev.xml | 64 ++++++++++++++ tests/qemuxml2xmltest.c | 3 + 17 files changed, 384 insertions(+), 87 deletions(-) create mode 100644 tests/qemuxml2argvdata/net-virtio-teaming-hostdev.args create mode 100644 tests/qemuxml2argvdata/net-virtio-teaming-hostdev.xml create mode 100644 tests/qemuxml2xmloutdata/net-virtio-teaming-hostdev.xml
Reviewed-by: Michal Privoznik <mprivozn@redhat.com> Michal
participants (2)
-
Laine Stump
-
Michal Privoznik