render / rpms / libvirt

Forked from rpms/libvirt 9 months ago
Clone
Blob Blame History Raw
From 16ba1d0258765d9c3b5e2da666ed6d4d933e26d9 Mon Sep 17 00:00:00 2001
Message-Id: <16ba1d0258765d9c3b5e2da666ed6d4d933e26d9@dist-git>
From: Laine Stump <laine@redhat.com>
Date: Thu, 30 Jan 2020 14:12:41 -0500
Subject: [PATCH] qemu: support interface <teaming> functionality
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The QEMU driver uses the <teaming type='persistent|transient'
persistent='blah'/> element to setup a "failover" pair of devices -
the persistent device must be a virtio emulated NIC, with the only
extra configuration being the addition of ",failover=on" to the device
commandline, and the transient device must be a hostdev NIC
(<interface type='hostdev'> or <interface type='network'> with a
network that is a pool of SRIOV VFs) where the extra configuration is
the addition of ",failover_pair_id=$aliasOfVirtio" to the device
commandline. These new options are supported in QEMU 4.2.0 and later.

Extra qemu-specific validation is added to ensure that the device
type/model is appropriate and that the qemu binary supports these
commandline options.

The result of this will be:

1) The virtio device presented to the guest will have an extra bit set
in its PCI capabilities indicating that it can be used as a failover
backup device. The virtio guest driver will need to be equipped to do
something with this information - this is included in the Linux
virtio-net driver in kernel 4.18 and above (and also backported to
some older distro kernels). Unfortunately there is no way for libvirt
to learn whether or not the guest driver supports failover - if it
doesn't then the extra PCI capability will be ignored and the guest OS
will just see two independent devices. (NB: the current virtio guest
driver also requires that the MAC addresses of the two NICs match in
order to pair them into a bond).

2) When a migration is requested, QEMu will automatically unplug the
transient/hostdev NIC from the guest on the source host before
starting migration, and automatically re-plug a similar device after
restarting the guest CPUs on the destination host. While the transient
NIC is unplugged, all network traffic will go through the
persistent/virtio device, but when the hostdev NIC is plugged in, it
will get all the traffic. This means that in normal circumstances the
guest gets the performance advantage of vfio-assigned "real hardware"
networking, but it can still be migrated with the only downside being
a performance penalty (due to using an emulated NIC) during the
migration.

Signed-off-by: Laine Stump <laine@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
(cherry picked from commit eb9f6cc4b3464707cf689fda9812e5129003bf27)

https://bugzilla.redhat.com/1693587
Signed-off-by: Laine Stump <laine@redhat.com>
Message-Id: <20200130191244.24174-4-laine@redhat.com>
Reviewed-by: Jiri Denemark <jdenemar@redhat.com>
---
 src/qemu/qemu_command.c                       |  9 +++++
 src/qemu/qemu_domain.c                        | 36 +++++++++++++++--
 .../qemuxml2argvdata/net-virtio-teaming.args  | 40 +++++++++++++++++++
 tests/qemuxml2argvtest.c                      |  4 ++
 4 files changed, 86 insertions(+), 3 deletions(-)
 create mode 100644 tests/qemuxml2argvdata/net-virtio-teaming.args

diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 7a184c229e..d144855b0d 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -3833,6 +3833,8 @@ qemuBuildNicDevStr(virDomainDefPtr def,
         }
         virBufferAsprintf(&buf, ",host_mtu=%u", net->mtu);
     }
+    if (usingVirtio && net->teaming.type == VIR_DOMAIN_NET_TEAMING_TYPE_PERSISTENT)
+       virBufferAddLit(&buf, ",failover=on");
 
     virBufferAsprintf(&buf, ",netdev=host%s", net->info.alias);
     virBufferAsprintf(&buf, ",id=%s", net->info.alias);
@@ -4704,6 +4706,13 @@ 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) {
+        virBufferAsprintf(&buf,  ",failover_pair_id=%s",
+                          dev->parentnet->teaming.persistent);
+    }
+
     return virBufferContentAndReset(&buf);
 }
 
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index 91a9f0481b..e37404340f 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -6391,12 +6391,20 @@ qemuDomainValidateActualNetDef(const virDomainNetDef *net,
         return -1;
     }
 
+    if (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'"),
+                       macstr, virDomainNetTypeToString(actualType));
+        return -1;
+    }
     return 0;
 }
 
 
 static int
-qemuDomainDeviceDefValidateNetwork(const virDomainNetDef *net)
+qemuDomainDeviceDefValidateNetwork(const virDomainNetDef *net,
+                                   virQEMUCapsPtr qemuCaps)
 {
     bool hasIPv4 = false;
     bool hasIPv6 = false;
@@ -6481,7 +6489,29 @@ qemuDomainDeviceDefValidateNetwork(const virDomainNetDef *net)
         return -1;
     }
 
-    if (net->coalesce && !qemuDomainNetSupportsCoalesce(net->type)) {
+    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 && !qemuDomainNetSupportsCoalesce(net->type)) {
         virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                        _("coalesce settings on interface type %s are not supported"),
                        virDomainNetTypeToString(net->type));
@@ -8377,7 +8407,7 @@ qemuDomainDeviceDefValidate(const virDomainDeviceDef *dev,
 
     switch ((virDomainDeviceType)dev->type) {
     case VIR_DOMAIN_DEVICE_NET:
-        ret = qemuDomainDeviceDefValidateNetwork(dev->data.net);
+        ret = qemuDomainDeviceDefValidateNetwork(dev->data.net, qemuCaps);
         break;
 
     case VIR_DOMAIN_DEVICE_CHR:
diff --git a/tests/qemuxml2argvdata/net-virtio-teaming.args b/tests/qemuxml2argvdata/net-virtio-teaming.args
new file mode 100644
index 0000000000..19e7260843
--- /dev/null
+++ b/tests/qemuxml2argvdata/net-virtio-teaming.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 b923590930..4d26fe0b55 100644
--- a/tests/qemuxml2argvtest.c
+++ b/tests/qemuxml2argvtest.c
@@ -1308,6 +1308,10 @@ mymain(void)
             QEMU_CAPS_VIRTIO_NET_RX_QUEUE_SIZE,
             QEMU_CAPS_VIRTIO_NET_TX_QUEUE_SIZE);
     DO_TEST_PARSE_ERROR("net-virtio-rxqueuesize-invalid-size", NONE);
+    DO_TEST("net-virtio-teaming",
+            QEMU_CAPS_VIRTIO_NET_FAILOVER,
+            QEMU_CAPS_DEVICE_VFIO_PCI);
+    DO_TEST_PARSE_ERROR("net-virtio-teaming", NONE);
     DO_TEST("net-eth", NONE);
     DO_TEST("net-eth-ifname", NONE);
     DO_TEST("net-eth-names", NONE);
-- 
2.25.0