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 | 176 ++++++++++++++++++++++++++++++++++++++++++++++-
src/util/virhostdev.h | 9 +++
5 files changed, 211 insertions(+), 1 deletion(-)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 48c86a2..97f81ee 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1709,6 +1709,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..681f720 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,176 @@ virHostdevPrepareSCSIVHostDevices(virHostdevManagerPtr mgr,
return -1;
}
+
+static bool
+virHostdevMediatedDeviceIsUsed(virMediatedDevicePtr dev,
+ virMediatedDeviceListPtr list)
+{
+ const char *drvname, *domname;
+ virMediatedDevicePtr tmp = NULL;
+
+ virObjectLock(list);
+
+ if ((tmp = virMediatedDeviceListFind(list, dev))) {
+ virMediatedDeviceGetUsedBy(tmp, &drvname, &domname);
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("Mediated device %s is in use by "
+ "driver %s, domain %s"),
+ virMediatedDeviceGetPath(tmp),
+ drvname, domname);
+ }
+
+ virObjectUnlock(list);
+ return !!tmp;
+}
+
+
+static int
+virHostdevMediatedDeviceCheckModel(virMediatedDevicePtr dev,
+ virMediatedDeviceModelType model)
+{
+ int ret = -1;
+ char *dev_api = NULL;
+ int actual_model;
+
+ if (virMediatedDeviceGetDeviceAPI(dev, &dev_api) < 0)
+ return -1;
+
+ /* safeguard in case we've got an older libvirt which doesn't know newer
+ * device_api models yet
+ */
+ if ((actual_model = virMediatedDeviceModelTypeFromString(dev_api)) <= 0)
+ goto cleanup;
+
+ if (actual_model != model) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Invalid device API '%s' for device %s: "
+ "device only supports '%s'"),
+ virMediatedDeviceModelTypeToString(model),
+ virMediatedDeviceGetPath(dev), dev_api);
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ VIR_FREE(dev_api);
+ return ret;
+}
+
+
+static int
+virHostdevMarkMediatedDevices(virHostdevManagerPtr mgr,
+ const char *drvname,
+ const char *domname,
+ virMediatedDeviceListPtr list)
+{
+ int ret = -1;
+ size_t i, j, count;
+
+ virObjectLock(mgr->activeMediatedHostdevs);
+
+ count = virMediatedDeviceListCount(list);
+ for (i = 0; i < count; i++) {
+ virMediatedDevicePtr mdev = virMediatedDeviceListGet(list, i);
+ VIR_DEBUG("Adding %s to activeMediatedHostdevs",
+ virMediatedDeviceGetPath(mdev));
+
+ virMediatedDeviceSetUsedBy(mdev, drvname, domname);
+
+ /* Copy mdev references to the driver list:
+ * - caller is responsible for NOT freeing devices in @list on success
+ * - we're responsible for performing a rollback on failure
+ */
+ if (virMediatedDeviceListAdd(mgr->activeMediatedHostdevs, mdev) < 0)
+ goto rollback;
+ }
+
+ ret = 0;
+ cleanup:
+ virObjectUnlock(mgr->activeMediatedHostdevs);
+ return ret;
+
+ rollback:
+ for (j = 0; j < i; j++) {
+ virMediatedDevicePtr tmp = virMediatedDeviceListGet(list, j);
+ virMediatedDeviceListSteal(mgr->activeMediatedHostdevs, tmp);
+ }
+ goto cleanup;
+}
+
+
+int
+virHostdevPrepareMediatedDevices(virHostdevManagerPtr mgr,
+ const char *drv_name,
+ const char *dom_name,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs)
+{
+ size_t i;
+ int ret = -1;
+ virMediatedDeviceListPtr list;
+
+ if (!nhostdevs)
+ return 0;
+
+ /* To prevent situation where mediated device is assigned to multiple
+ * domains we maintain a driver list of currently assigned mediated devices.
+ * A device is appended to the driver list after a series of preparations.
+ */
+ if (!(list = virMediatedDeviceListNew()))
+ goto cleanup;
+
+ /* Loop 1: Build a temporary list of unused mediated devices. */
+ for (i = 0; i < nhostdevs; i++) {
+ virDomainHostdevDefPtr hostdev = hostdevs[i];
+ virDomainHostdevSubsysMediatedDevPtr src =
&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(src->uuidstr)))
+ goto cleanup;
+
+ if (virHostdevMediatedDeviceIsUsed(mdev, mgr->activeMediatedHostdevs))
+ goto cleanup;
+
+ /* Check whether the user-provided model corresponds with the actually
+ * supported mediated device's API.
+ */
+ if (virHostdevMediatedDeviceCheckModel(mdev, src->model) < 0)
+ goto cleanup;
+
+ if (virMediatedDeviceListAdd(list, mdev) < 0) {
+ virMediatedDeviceFree(mdev);
+ goto cleanup;
+ }
+ }
+
+
+ /* Mark the devices in the list as used by @drv_name-@dom_name and copy the
+ * references to the driver list
+ */
+ 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) {
+ virMediatedDevicePtr 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