Keep track of the assigned mediated devices the same way we do it for
the rest of hostdevs.
Signed-off-by: Erik Skultety <eskultet(a)redhat.com>
---
src/libvirt_private.syms | 1 +
src/qemu/qemu_hostdev.c | 22 +++++++++
src/qemu/qemu_hostdev.h | 4 ++
src/util/virhostdev.c | 126 ++++++++++++++++++++++++++++++++++++++++++++++-
src/util/virhostdev.h | 9 ++++
5 files changed, 161 insertions(+), 1 deletion(-)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 4047945..8921a53 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1707,6 +1707,7 @@ virHostdevPCINodeDeviceDetach;
virHostdevPCINodeDeviceReAttach;
virHostdevPCINodeDeviceReset;
virHostdevPrepareDomainDevices;
+virHostdevPrepareMediatedDevices;
virHostdevPreparePCIDevices;
virHostdevPrepareSCSIDevices;
virHostdevPrepareSCSIVHostDevices;
diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c
index 7cd49e4..45b731c 100644
--- a/src/qemu/qemu_hostdev.c
+++ b/src/qemu/qemu_hostdev.c
@@ -305,6 +305,24 @@ qemuHostdevPrepareSCSIVHostDevices(virQEMUDriverPtr driver,
}
int
+qemuHostdevPrepareMediatedDevices(virQEMUDriverPtr driver,
+ const char *name,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs)
+{
+ virHostdevManagerPtr hostdev_mgr = driver->hostdevMgr;
+
+ if (!qemuHostdevHostSupportsPassthroughVFIO()) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("host doesn't support VFIO PCI interface"));
+ return -1;
+ }
+
+ return virHostdevPrepareMediatedDevices(hostdev_mgr, QEMU_DRIVER_NAME,
+ name, hostdevs, nhostdevs);
+}
+
+int
qemuHostdevPrepareDomainDevices(virQEMUDriverPtr driver,
virDomainDefPtr def,
virQEMUCapsPtr qemuCaps,
@@ -330,6 +348,10 @@ qemuHostdevPrepareDomainDevices(virQEMUDriverPtr driver,
def->hostdevs, def->nhostdevs) < 0)
return -1;
+ if (qemuHostdevPrepareMediatedDevices(driver, def->name,
+ def->hostdevs, def->nhostdevs) < 0)
+ return -1;
+
return 0;
}
diff --git a/src/qemu/qemu_hostdev.h b/src/qemu/qemu_hostdev.h
index 74a7d4f..9399241 100644
--- a/src/qemu/qemu_hostdev.h
+++ b/src/qemu/qemu_hostdev.h
@@ -59,6 +59,10 @@ int qemuHostdevPrepareSCSIVHostDevices(virQEMUDriverPtr driver,
const char *name,
virDomainHostdevDefPtr *hostdevs,
int nhostdevs);
+int qemuHostdevPrepareMediatedDevices(virQEMUDriverPtr driver,
+ const char *name,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs);
int qemuHostdevPrepareDomainDevices(virQEMUDriverPtr driver,
virDomainDefPtr def,
virQEMUCapsPtr qemuCaps,
diff --git a/src/util/virhostdev.c b/src/util/virhostdev.c
index 86ca8e0..7691eaa 100644
--- a/src/util/virhostdev.c
+++ b/src/util/virhostdev.c
@@ -1,6 +1,6 @@
/* virhostdev.c: hostdev management
*
- * Copyright (C) 2006-2007, 2009-2016 Red Hat, Inc.
+ * Copyright (C) 2006-2007, 2009-2017 Red Hat, Inc.
* Copyright (C) 2006 Daniel P. Berrange
* Copyright (C) 2014 SUSE LINUX Products GmbH, Nuernberg, Germany.
*
@@ -147,6 +147,7 @@ virHostdevManagerDispose(void *obj)
virObjectUnref(hostdevMgr->activeUSBHostdevs);
virObjectUnref(hostdevMgr->activeSCSIHostdevs);
virObjectUnref(hostdevMgr->activeSCSIVHostHostdevs);
+ virObjectUnref(hostdevMgr->activeMediatedHostdevs);
VIR_FREE(hostdevMgr->stateDir);
}
@@ -174,6 +175,9 @@ virHostdevManagerNew(void)
if (!(hostdevMgr->activeSCSIVHostHostdevs = virSCSIVHostDeviceListNew()))
goto error;
+ if (!(hostdevMgr->activeMediatedHostdevs = virMediatedDeviceListNew()))
+ goto error;
+
if (privileged) {
if (VIR_STRDUP(hostdevMgr->stateDir, HOSTDEV_STATE_DIR) < 0)
goto error;
@@ -1595,6 +1599,126 @@ virHostdevPrepareSCSIVHostDevices(virHostdevManagerPtr mgr,
return -1;
}
+static int
+virHostdevMarkMediatedDevices(virHostdevManagerPtr mgr,
+ const char *drvname,
+ const char *domname,
+ virMediatedDeviceListPtr list)
+{
+ size_t i, j;
+ unsigned int count;
+ virMediatedDevicePtr tmp;
+
+ virObjectLock(mgr->activeMediatedHostdevs);
+ count = virMediatedDeviceListCount(list);
+
+ for (i = 0; i < count; i++) {
+ virMediatedDevicePtr mdev = virMediatedDeviceListGet(list, i);
+ if ((tmp = virMediatedDeviceListFind(mgr->activeMediatedHostdevs,
+ mdev))) {
+ char *tmp_drvname;
+ char *tmp_domname;
+
+ virMediatedDeviceGetUsedBy(tmp, &tmp_drvname, &tmp_domname);
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("Mediated device %s is in use by "
+ "driver %s, domain %s"),
+ virMediatedDeviceGetPath(tmp),
+ tmp_drvname, tmp_domname);
+ goto error;
+ }
+
+ virMediatedDeviceSetUsedBy(mdev, drvname, domname);
+ VIR_DEBUG("Adding %s to activeMediatedHostdevs",
+ virMediatedDeviceGetPath(mdev));
+ /*
+ * The caller is responsible to steal these mdev devices
+ * from the virMediatedDeviceList that passed in on success,
+ * perform rollback on failure.
+ */
+ if (virMediatedDeviceListAdd(mgr->activeMediatedHostdevs, mdev) < 0)
+ goto error;
+ }
+
+ virObjectUnlock(mgr->activeMediatedHostdevs);
+ return 0;
+
+ error:
+ for (j = 0; j < i; j++) {
+ tmp = virMediatedDeviceListGet(list, i);
+ virMediatedDeviceListSteal(mgr->activeMediatedHostdevs, tmp);
+ }
+ virObjectUnlock(mgr->activeMediatedHostdevs);
+ return -1;
+}
+
+int
+virHostdevPrepareMediatedDevices(virHostdevManagerPtr mgr,
+ const char *drv_name,
+ const char *dom_name,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs)
+{
+ size_t i;
+ int ret = -1;
+ virMediatedDeviceListPtr list;
+ virMediatedDevicePtr tmp;
+
+ if (!nhostdevs)
+ return 0;
+
+ /* To prevent situation where Mediated device is assigned to two domains
+ * we need to keep a list of currently assigned Mediated devices.
+ * This is done in several loops which cannot be joined into one big
+ * loop. See virHostdevPreparePCIDevices()
+ */
+ if (!(list = virMediatedDeviceListNew()))
+ goto cleanup;
+
+ /* Loop 1: build temporary list
+ */
+ for (i = 0; i < nhostdevs; i++) {
+ virDomainHostdevDefPtr hostdev = hostdevs[i];
+ virDomainHostdevSubsysMediatedDevPtr mdevsrc =
&hostdev->source.subsys.u.mdev;
+ virMediatedDevicePtr mdev;
+
+ if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
+ continue;
+ if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV)
+ continue;
+
+ if (!(mdev = virMediatedDeviceNew(&mdevsrc->addr, mdevsrc->uuidstr)))
+ goto cleanup;
+
+ if (virMediatedDeviceListAdd(list, mdev) < 0) {
+ virMediatedDeviceFree(mdev);
+ goto cleanup;
+ }
+ }
+
+ /* Mark devices in temporary list as used by @dom_name
+ * and add them do driver list. However, if something goes
+ * wrong, perform rollback.
+ */
+ if (virHostdevMarkMediatedDevices(mgr, drv_name, dom_name, list) < 0)
+ goto cleanup;
+
+ /* Loop 2: Temporary list was successfully merged with
+ * driver list, so steal all items to avoid freeing them
+ * in cleanup label.
+ */
+ while (virMediatedDeviceListCount(list) > 0) {
+ tmp = virMediatedDeviceListGet(list, 0);
+ virMediatedDeviceListSteal(list, tmp);
+ }
+
+ ret = 0;
+
+ cleanup:
+ virObjectUnref(list);
+ return ret;
+}
+
void
virHostdevReAttachUSBDevices(virHostdevManagerPtr mgr,
const char *drv_name,
diff --git a/src/util/virhostdev.h b/src/util/virhostdev.h
index 4c1fea3..b077089 100644
--- a/src/util/virhostdev.h
+++ b/src/util/virhostdev.h
@@ -31,6 +31,7 @@
# include "virusb.h"
# include "virscsi.h"
# include "virscsivhost.h"
+# include "virmdev.h"
# include "domain_conf.h"
typedef enum {
@@ -55,6 +56,7 @@ struct _virHostdevManager {
virUSBDeviceListPtr activeUSBHostdevs;
virSCSIDeviceListPtr activeSCSIHostdevs;
virSCSIVHostDeviceListPtr activeSCSIVHostHostdevs;
+ virMediatedDeviceListPtr activeMediatedHostdevs;
};
virHostdevManagerPtr virHostdevManagerGetDefault(void);
@@ -96,6 +98,13 @@ virHostdevPrepareSCSIVHostDevices(virHostdevManagerPtr hostdev_mgr,
virDomainHostdevDefPtr *hostdevs,
int nhostdevs)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
+int
+virHostdevPrepareMediatedDevices(virHostdevManagerPtr hostdev_mgr,
+ const char *drv_name,
+ const char *dom_name,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
void
virHostdevReAttachPCIDevices(virHostdevManagerPtr hostdev_mgr,
const char *drv_name,
--
2.10.2