From nobody Fri May 16 03:21:16 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) client-ip=209.132.183.28; envelope-from=libvir-list-bounces@redhat.com; helo=mx1.redhat.com; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com Return-Path: Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by mx.zohomail.com with SMTPS id 1500132705855122.57468868503986; Sat, 15 Jul 2017 08:31:45 -0700 (PDT) Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 22644356C0; Sat, 15 Jul 2017 15:31:44 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.21]) by smtp.corp.redhat.com (Postfix) with ESMTPS id E88CA1710D; Sat, 15 Jul 2017 15:31:43 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id A3D0B4A491; Sat, 15 Jul 2017 15:31:43 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id v6FFVHFJ006854 for ; Sat, 15 Jul 2017 11:31:17 -0400 Received: by smtp.corp.redhat.com (Postfix) id 51CB278C14; Sat, 15 Jul 2017 15:31:17 +0000 (UTC) Received: from inaba.usersys.redhat.com (ovpn-204-62.brq.redhat.com [10.40.204.62]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 47E72784D9; Sat, 15 Jul 2017 15:31:16 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 22644356C0 Authentication-Results: ext-mx06.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx06.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=libvir-list-bounces@redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.redhat.com 22644356C0 From: Andrea Bolognani To: libvir-list@redhat.com Date: Sat, 15 Jul 2017 17:30:58 +0200 Message-Id: <1500132659-3658-4-git-send-email-abologna@redhat.com> In-Reply-To: <1500132659-3658-1-git-send-email-abologna@redhat.com> References: <1500132659-3658-1-git-send-email-abologna@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-loop: libvir-list@redhat.com Cc: laine@laine.org Subject: [libvirt] [PATCH v4 3/4] qemu: Isolate hostdevs on pSeries guests X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.30]); Sat, 15 Jul 2017 15:31:44 +0000 (UTC) X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" All the pieces are now in place, so we can finally start using isolation groups to achieve our initial goal, which is separating hostdevs from emulated PCI devices while keeping hostdevs that belong to the same host IOMMU group together. Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=3D1280542 Signed-off-by: Andrea Bolognani Reviewed-by: Laine Stump --- src/qemu/qemu_domain_address.c | 241 +++++++++++++++++= ++++ src/qemu/qemu_domain_address.h | 4 + src/qemu/qemu_hotplug.c | 7 + tests/qemumemlocktest.c | 2 +- .../qemuxml2argv-pseries-hostdevs-1.args | 8 +- .../qemuxml2argv-pseries-hostdevs-2.args | 3 +- .../qemuxml2argv-pseries-hostdevs-3.args | 2 +- .../qemuxml2xmlout-pseries-hostdevs-1.xml | 14 +- .../qemuxml2xmlout-pseries-hostdevs-2.xml | 6 +- .../qemuxml2xmlout-pseries-hostdevs-3.xml | 2 +- 10 files changed, 278 insertions(+), 11 deletions(-) diff --git a/src/qemu/qemu_domain_address.c b/src/qemu/qemu_domain_address.c index b247c85..2594712 100644 --- a/src/qemu/qemu_domain_address.c +++ b/src/qemu/qemu_domain_address.c @@ -25,6 +25,7 @@ =20 #include "qemu_domain_address.h" #include "qemu_domain.h" +#include "network/bridge_driver.h" #include "viralloc.h" #include "virerror.h" #include "virlog.h" @@ -906,6 +907,243 @@ qemuDomainFillAllPCIConnectFlags(virDomainDefPtr def, =20 =20 /** + * qemuDomainFindUnusedIsolationGroupIter: + * @def: domain definition + * @dev: device definition + * @info: device information + * @opaque: user data + * + * Used to implement qemuDomainFindUnusedIsolationGroup(). You probably + * don't want to call this directly. + * + * Return: 0 if the isolation group is not used by the device, <1 otherwis= e. + */ +static int +qemuDomainFindUnusedIsolationGroupIter(virDomainDefPtr def ATTRIBUTE_UNUSE= D, + virDomainDeviceDefPtr dev ATTRIBUTE= _UNUSED, + virDomainDeviceInfoPtr info, + void *opaque) +{ + unsigned int *isolationGroup =3D opaque; + + if (info->isolationGroup =3D=3D *isolationGroup) + return -1; + + return 0; +} + + +/** + * qemuDomainFindUnusedIsolationGroup: + * @def: domain definition + * + * Find an isolation group that is not used by any device in @def yet. + * + * Normally, we'd look up the device's IOMMU group and base its isolation + * group on that; however, when a network interface uses a network backed + * by SR-IOV Virtual Functions, we can't know at PCI address assignment + * time which host device will be used so we can't look up its IOMMU group. + * + * We still want such a device to be isolated: this function can be used + * to obtain a synthetic isolation group usable for the purpose. + * + * Return: unused isolation group + */ +static unsigned int +qemuDomainFindUnusedIsolationGroup(virDomainDefPtr def) +{ + unsigned int isolationGroup =3D UINT_MAX; + + /* We start from the highest possible isolation group and work our + * way backwards so that we're working in a completely different range + * from IOMMU groups, thus avoiding clashes. We're realistically going + * to call this function just a few times per guest anyway */ + while (isolationGroup > 0 && + virDomainDeviceInfoIterate(def, + qemuDomainFindUnusedIsolationGroupIt= er, + &isolationGroup) < 0) { + isolationGroup--; + } + + return isolationGroup; +} + + +/** + * qemuDomainFillDeviceIsolationGroup: + * @def: domain definition + * @dev: device definition + * + * Fill isolation group information for a single device. + * + * Return: 0 on success, <0 on failure + * */ +int +qemuDomainFillDeviceIsolationGroup(virDomainDefPtr def, + virDomainDeviceDefPtr dev) +{ + int ret =3D -1; + + /* Only host devices need their isolation group to be different from + * the default. Interfaces of type hostdev are just host devices in + * disguise, but we don't need to handle them separately because for + * each such interface a corresponding hostdev is also added to the + * guest configuration */ + if (dev->type =3D=3D VIR_DOMAIN_DEVICE_HOSTDEV) { + virDomainHostdevDefPtr hostdev =3D dev->data.hostdev; + virDomainDeviceInfoPtr info =3D hostdev->info; + virPCIDeviceAddressPtr hostAddr; + int tmp; + + /* Only PCI host devices are subject to isolation */ + if (hostdev->mode !=3D VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || + hostdev->source.subsys.type !=3D VIR_DOMAIN_HOSTDEV_SUBSYS_TYP= E_PCI) { + goto skip; + } + + hostAddr =3D &hostdev->source.subsys.u.pci.addr; + + /* If a non-default isolation has already been assigned to the + * device, we can avoid looking up the information again */ + if (info->isolationGroup > 0) + goto skip; + + /* The isolation group depends on the IOMMU group assigned by the = host */ + tmp =3D virPCIDeviceAddressGetIOMMUGroupNum(hostAddr); + + if (tmp < 0) { + VIR_WARN("Can't look up isolation group for host device " + "%04x:%02x:%02x.%x", + hostAddr->domain, hostAddr->bus, + hostAddr->slot, hostAddr->function); + goto cleanup; + } + + /* The isolation group for a host device is its IOMMU group, + * increased by one: this is because zero is a valid IOMMU group b= ut + * that's also the default isolation group, which we want to save + * for emulated devices. Shifting isolation groups for host devices + * by one ensures there is no overlap */ + info->isolationGroup =3D tmp + 1; + + VIR_DEBUG("Isolation group for host device %04x:%02x:%02x.%x is %u= ", + hostAddr->domain, hostAddr->bus, + hostAddr->slot, hostAddr->function, + info->isolationGroup); + + } else if (dev->type =3D=3D VIR_DOMAIN_DEVICE_NET) { + virDomainNetDefPtr iface =3D dev->data.net; + virDomainDeviceInfoPtr info =3D &iface->info; + unsigned int tmp; + + /* Network interfaces can ultimately result in the guest being + * assigned a host device if the libvirt network they're connected + * to is of type hostdev. Any other kind of network doesn't require + * us to isolate the guest device, so we can skip them */ + if (iface->type =3D=3D VIR_DOMAIN_NET_TYPE_NETWORK && + networkGetActualType(iface) !=3D VIR_DOMAIN_NET_TYPE_HOSTDEV) { + goto skip; + } + + /* If a non-default isolation has already been assigned to the + * device, we can avoid looking up the information again */ + if (info->isolationGroup > 0) + goto skip; + + /* Obtain a synthetic isolation group for the device, since at this + * point in time we don't have access to the IOMMU group of the ho= st + * device that will eventually be used by the guest */ + tmp =3D qemuDomainFindUnusedIsolationGroup(def); + + if (tmp =3D=3D 0) { + VIR_WARN("Can't obtain usable isolation group for interface " + "configured to use hostdev-backed network '%s'", + iface->data.network.name); + goto cleanup; + } + + info->isolationGroup =3D tmp; + + VIR_DEBUG("Isolation group for interface configured to use " + "hostdev-backed network '%s' is %u", + iface->data.network.name, info->isolationGroup); + } + + skip: + ret =3D 0; + + cleanup: + return ret; +} + + +/** + * qemuDomainFillDeviceIsolationGroupIter: + * @def: domain definition + * @dev: device definition + * @info: device information + * @opaque: user data + * + * A version of qemuDomainFillDeviceIsolationGroup() to be used + * with virDomainDeviceInfoIterate() + * + * Return: 0 on success, <0 on failure + */ +static int +qemuDomainFillDeviceIsolationGroupIter(virDomainDefPtr def, + virDomainDeviceDefPtr dev, + virDomainDeviceInfoPtr info ATTRIBU= TE_UNUSED, + void *opaque ATTRIBUTE_UNUSED) +{ + return qemuDomainFillDeviceIsolationGroup(def, dev); +} + + +/** + * qemuDomainSetupIsolationGroups: + * @def: domain definition + * + * High-level function to set up isolation groups for all devices + * and controllers in @def. Isolation groups will only be set up if + * the guest architecture and machine type require it, so this + * function can and should be called unconditionally before attempting + * to assign any PCI address. + * + * Return: 0 on success, <0 on failure + */ +static int +qemuDomainSetupIsolationGroups(virDomainDefPtr def) +{ + int idx; + int ret =3D -1; + + /* Only pSeries guests care about isolation groups at the moment */ + if (!qemuDomainIsPSeries(def)) + return 0; + + idx =3D virDomainControllerFind(def, VIR_DOMAIN_CONTROLLER_TYPE_PCI, 0= ); + if (idx < 0) + goto cleanup; + + /* We want to prevent hostdevs from being plugged into the default PHB: + * we can make sure that doesn't happen by locking its isolation group= */ + def->controllers[idx]->info.isolationGroupLocked =3D true; + + /* Fill in isolation groups for all other devices */ + if (virDomainDeviceInfoIterate(def, + qemuDomainFillDeviceIsolationGroupIter, + NULL) < 0) { + goto cleanup; + } + + ret =3D 0; + + cleanup: + return ret; +} + + +/** * qemuDomainFillDevicePCIConnectFlags: * * @def: the entire DomainDef @@ -2054,6 +2292,9 @@ qemuDomainAssignPCIAddresses(virDomainDefPtr def, if (qemuDomainFillAllPCIConnectFlags(def, qemuCaps, driver) < 0) goto cleanup; =20 + if (qemuDomainSetupIsolationGroups(def) < 0) + goto cleanup; + if (nbuses > 0) { /* 1st pass to figure out how many PCI bridges we need */ if (!(addrs =3D qemuDomainPCIAddressSetCreate(def, nbuses, true))) diff --git a/src/qemu/qemu_domain_address.h b/src/qemu/qemu_domain_address.h index 067f4e7..b5644fa 100644 --- a/src/qemu/qemu_domain_address.h +++ b/src/qemu/qemu_domain_address.h @@ -44,6 +44,10 @@ int qemuDomainEnsurePCIAddress(virDomainObjPtr obj, virQEMUDriverPtr driver) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); =20 +int qemuDomainFillDeviceIsolationGroup(virDomainDefPtr def, + virDomainDeviceDefPtr dev) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); + void qemuDomainReleaseDeviceAddress(virDomainObjPtr vm, virDomainDeviceInfoPtr info, const char *devstr); diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 4bc4972..da5aafa 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1476,6 +1476,13 @@ qemuDomainAttachHostPCIDevice(virQEMUDriverPtr drive= r, =20 if (qemuAssignDeviceHostdevAlias(vm->def, &info->alias, -1) < 0) goto error; + + if (qemuDomainIsPSeries(vm->def)) { + /* Isolation groups are only relevant for pSeries guests */ + if (qemuDomainFillDeviceIsolationGroup(vm->def, &dev) < 0) + goto error; + } + if (qemuDomainEnsurePCIAddress(vm, &dev, driver) < 0) goto error; releaseaddr =3D true; diff --git a/tests/qemumemlocktest.c b/tests/qemumemlocktest.c index c0f1dc3..268563d 100644 --- a/tests/qemumemlocktest.c +++ b/tests/qemumemlocktest.c @@ -131,7 +131,7 @@ mymain(void) =20 DO_TEST("pseries-hardlimit", 2147483648); DO_TEST("pseries-locked", VIR_DOMAIN_MEMORY_PARAM_UNLIMITED); - DO_TEST("pseries-hostdev", 2168455168); + DO_TEST("pseries-hostdev", 4320133120); =20 DO_TEST("pseries-hardlimit+locked", 2147483648); DO_TEST("pseries-hardlimit+hostdev", 2147483648); diff --git a/tests/qemuxml2argvdata/qemuxml2argv-pseries-hostdevs-1.args b/= tests/qemuxml2argvdata/qemuxml2argv-pseries-hostdevs-1.args index 051ffde..8a4a4c5 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-pseries-hostdevs-1.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-pseries-hostdevs-1.args @@ -18,6 +18,8 @@ QEMU_AUDIO_DRV=3Dnone \ server,nowait \ -mon chardev=3Dcharmonitor,id=3Dmonitor,mode=3Dreadline \ -boot c \ --device vfio-pci,host=3D0005:90:01.0,id=3Dhostdev0,bus=3Dpci.0,addr=3D0x1 \ --device vfio-pci,host=3D0001:01:00.0,id=3Dhostdev1,bus=3Dpci.0,addr=3D0x2 \ --device vfio-pci,host=3D0001:01:00.1,id=3Dhostdev2,bus=3Dpci.0,addr=3D0x3 +-device spapr-pci-host-bridge,index=3D1,id=3Dpci.1 \ +-device spapr-pci-host-bridge,index=3D2,id=3Dpci.2 \ +-device vfio-pci,host=3D0005:90:01.0,id=3Dhostdev0,bus=3Dpci.1.0,addr=3D0x= 1 \ +-device vfio-pci,host=3D0001:01:00.0,id=3Dhostdev1,bus=3Dpci.2.0,addr=3D0x= 1 \ +-device vfio-pci,host=3D0001:01:00.1,id=3Dhostdev2,bus=3Dpci.2.0,addr=3D0x2 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-pseries-hostdevs-2.args b/= tests/qemuxml2argvdata/qemuxml2argv-pseries-hostdevs-2.args index 83d4306..cd5b664 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-pseries-hostdevs-2.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-pseries-hostdevs-2.args @@ -19,6 +19,7 @@ server,nowait \ -mon chardev=3Dcharmonitor,id=3Dmonitor,mode=3Dreadline \ -boot c \ -device spapr-pci-host-bridge,index=3D1,id=3Dpci.1 \ +-device spapr-pci-host-bridge,index=3D2,id=3Dpci.2 \ -device virtio-scsi-pci,id=3Dscsi0,bus=3Dpci.1.0,addr=3D0x1 \ -device vfio-pci,host=3D0001:01:00.0,id=3Dhostdev0,bus=3Dpci.1.0,addr=3D0x= 2 \ --device vfio-pci,host=3D0005:90:01.0,id=3Dhostdev1,bus=3Dpci.0,addr=3D0x1 +-device vfio-pci,host=3D0005:90:01.0,id=3Dhostdev1,bus=3Dpci.2.0,addr=3D0x1 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-pseries-hostdevs-3.args b/= tests/qemuxml2argvdata/qemuxml2argv-pseries-hostdevs-3.args index eda6cc7..66a31ba 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-pseries-hostdevs-3.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-pseries-hostdevs-3.args @@ -21,4 +21,4 @@ server,nowait \ -device spapr-pci-host-bridge,index=3D1,id=3Dpci.1 \ -device spapr-pci-host-bridge,index=3D2,id=3Dpci.2 \ -device vfio-pci,host=3D0001:01:00.0,id=3Dhostdev0,bus=3Dpci.2.0,addr=3D0x= 1 \ --device vfio-pci,host=3D0001:01:00.1,id=3Dhostdev1,bus=3Dpci.0,addr=3D0x1 +-device vfio-pci,host=3D0001:01:00.1,id=3Dhostdev1,bus=3Dpci.2.0,addr=3D0x2 diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-pseries-hostdevs-1.xml= b/tests/qemuxml2xmloutdata/qemuxml2xmlout-pseries-hostdevs-1.xml index fa9e4da..e77a060 100644 --- a/tests/qemuxml2xmloutdata/qemuxml2xmlout-pseries-hostdevs-1.xml +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-pseries-hostdevs-1.xml @@ -19,27 +19,35 @@ + + + + + + + +
-
+
-
+
-
+
diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-pseries-hostdevs-2.xml= b/tests/qemuxml2xmloutdata/qemuxml2xmlout-pseries-hostdevs-2.xml index 17ff4c8..cfa395b 100644 --- a/tests/qemuxml2xmloutdata/qemuxml2xmlout-pseries-hostdevs-2.xml +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-pseries-hostdevs-2.xml @@ -26,6 +26,10 @@ + + + + @@ -38,7 +42,7 @@
-
+
diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-pseries-hostdevs-3.xml= b/tests/qemuxml2xmloutdata/qemuxml2xmlout-pseries-hostdevs-3.xml index 58023ec..f91959b 100644 --- a/tests/qemuxml2xmloutdata/qemuxml2xmlout-pseries-hostdevs-3.xml +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-pseries-hostdevs-3.xml @@ -39,7 +39,7 @@
-
+
--=20 2.7.5 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list