Extract code from qemu_hostdev.c and make it reusable for multiple
drivers,
meanwhile maintain a global hostdev state to solve conflict between
different
drivers.
Signed-off-by: Chunyan Liu <cyliu(a)suse.com>
---
po/POTFILES.in | 1 +
src/Makefile.am | 1 +
src/libvirt_private.syms | 21 +
src/lxc/lxc_hostdev.c | 11 +-
src/qemu/qemu_driver.c | 4 +-
src/qemu/qemu_hostdev.c | 42 +-
src/util/virhostdev.c | 1691
++++++++++++++++++++++++++++++++++++++++++++++
src/util/virhostdev.h | 134 ++++
src/util/virpci.c | 30 +-
src/util/virpci.h | 9 +-
src/util/virscsi.c | 28 +-
src/util/virscsi.h | 8 +-
src/util/virusb.c | 29 +-
src/util/virusb.h | 8 +-
14 files changed, 1969 insertions(+), 48 deletions(-)
create mode 100644 src/util/virhostdev.c
create mode 100644 src/util/virhostdev.h
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 49dfc9c..ba63428 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -161,6 +161,7 @@ src/util/vireventpoll.c
src/util/virfile.c
src/util/virhash.c
src/util/virhook.c
+src/util/virhostdev.c
src/util/viridentity.c
src/util/virinitctl.c
src/util/viriptables.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 57e163f..98233cd 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -104,6 +104,7 @@ UTIL_SOURCES = \
util/virhash.c util/virhash.h \
util/virhashcode.c util/virhashcode.h \
util/virhook.c util/virhook.h \
+ util/virhostdev.c util/virhostdev.h \
util/viridentity.c util/viridentity.h \
util/virinitctl.c util/virinitctl.h \
util/viriptables.c util/viriptables.h \
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 2dbb8f8..dcb0a3e 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1264,6 +1264,27 @@ virHookInitialize;
virHookPresent;
+#util/virhostdev.h
+virHostdevHostSupportsPassthroughKVM;
+virHostdevHostSupportsPassthroughVFIO;
+virHostdevManagerGetDefault;
+virHostdevPciNodeDeviceDetach;
+virHostdevPciNodeDeviceReAttach;
+virHostdevPciNodeDeviceReset;
+virHostdevPrepareDomainHostdevs;
+virHostdevPreparePciHostdevs;
+virHostdevPrepareScsiHostdevs;
+virHostdevPrepareUsbHostdevs;
+virHostdevReAttachDomainHostdevs;
+virHostdevReAttachPciHostdevs;
+virHostdevReAttachScsiHostdevs;
+virHostdevReAttachUsbHostdevs;
+virHostdevUpdateActiveHostdevs;
+virHostdevUpdateActivePciHostdevs;
+virHostdevUpdateActiveScsiHostdevs;
+virHostdevUpdateActiveUsbHostdevs;
+
+
# util/viridentity.h
virIdentityGetAttr;
virIdentityGetCurrent;
diff --git a/src/lxc/lxc_hostdev.c b/src/lxc/lxc_hostdev.c
index 3b371fc..77ce965 100644
--- a/src/lxc/lxc_hostdev.c
+++ b/src/lxc/lxc_hostdev.c
@@ -60,7 +60,7 @@ virLXCUpdateActiveUsbHostdevs(virLXCDriverPtr driver,
continue;
}
- virUSBDeviceSetUsedBy(usb, def->name);
+ virUSBDeviceSetUsedBy(usb, "QEMU", def->name);
virObjectLock(driver->activeUsbHostdevs);
if (virUSBDeviceListAdd(driver->activeUsbHostdevs, usb) < 0) {
@@ -90,7 +90,9 @@ virLXCPrepareHostdevUSBDevices(virLXCDriverPtr driver,
for (i = 0; i < count; i++) {
virUSBDevicePtr usb = virUSBDeviceListGet(list, i);
if ((tmp = virUSBDeviceListFind(driver->activeUsbHostdevs, usb))) {
- const char *other_name = virUSBDeviceGetUsedBy(tmp);
+ const char *other_name = NULL;
+ const char *other_drvname = NULL;
+ virUSBDeviceGetUsedBy(tmp, &other_drvname, &other_name);
if (other_name)
virReportError(VIR_ERR_OPERATION_INVALID,
@@ -103,7 +105,7 @@ virLXCPrepareHostdevUSBDevices(virLXCDriverPtr driver,
goto error;
}
- virUSBDeviceSetUsedBy(usb, name);
+ virUSBDeviceSetUsedBy(usb, "QEMU", name);
VIR_DEBUG("Adding %03d.%03d dom=%s to activeUsbHostdevs",
virUSBDeviceGetBus(usb), virUSBDeviceGetDevno(usb),
name);
/*
@@ -352,6 +354,7 @@ virLXCDomainReAttachHostUsbDevices(virLXCDriverPtr
driver,
virDomainHostdevDefPtr hostdev = hostdevs[i];
virUSBDevicePtr usb, tmp;
const char *used_by = NULL;
+ const char *used_by_drvname = NULL;
if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
continue;
@@ -389,7 +392,7 @@ virLXCDomainReAttachHostUsbDevices(virLXCDriverPtr
driver,
continue;
}
- used_by = virUSBDeviceGetUsedBy(tmp);
+ virUSBDeviceGetUsedBy(tmp, &used_by_drvname, &used_by);
if (STREQ_NULLABLE(used_by, name)) {
VIR_DEBUG("Removing %03d.%03d dom=%s from activeUsbHostdevs",
hostdev->source.subsys.u.usb.bus,
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 45d11cd..90820ab 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -10885,7 +10885,9 @@ qemuNodeDeviceReAttach(virNodeDevicePtr dev)
virObjectLock(driver->inactivePciHostdevs);
other = virPCIDeviceListFind(driver->activePciHostdevs, pci);
if (other) {
- const char *other_name = virPCIDeviceGetUsedBy(other);
+ const char *other_name = NULL;
+ const char *other_drvname = NULL;
+ virPCIDeviceGetUsedBy(other, &other_drvname, &other_name);
if (other_name)
virReportError(VIR_ERR_OPERATION_INVALID,
diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c
index dee61e7..1dc652b 100644
--- a/src/qemu/qemu_hostdev.c
+++ b/src/qemu/qemu_hostdev.c
@@ -177,7 +177,7 @@ qemuUpdateActivePciHostdevs(virQEMUDriverPtr driver,
goto cleanup;
}
- virPCIDeviceSetUsedBy(dev, def->name);
+ virPCIDeviceSetUsedBy(dev, "QEMU", def->name);
/* Setup the original states for the PCI device */
virPCIDeviceSetUnbindFromStub(dev,
hostdev->origstates.states.pci.unbind_from_stub);
@@ -230,7 +230,7 @@ qemuUpdateActiveUsbHostdevs(virQEMUDriverPtr driver,
continue;
}
- virUSBDeviceSetUsedBy(usb, def->name);
+ virUSBDeviceSetUsedBy(usb, "QEMU", def->name);
if (virUSBDeviceListAdd(driver->activeUsbHostdevs, usb) < 0) {
virUSBDeviceFree(usb);
@@ -270,7 +270,7 @@ qemuUpdateActiveScsiHostdevs(virQEMUDriverPtr driver,
hostdev->readonly)))
goto cleanup;
- virSCSIDeviceSetUsedBy(scsi, def->name);
+ virSCSIDeviceSetUsedBy(scsi, "QEMU", def->name);
if (virSCSIDeviceListAdd(driver->activeScsiHostdevs, scsi) < 0) {
virSCSIDeviceFree(scsi);
@@ -683,7 +683,9 @@ qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver,
* the dev is in list driver->activePciHostdevs.
*/
if ((other = virPCIDeviceListFind(driver->activePciHostdevs, dev))) {
- const char *other_name = virPCIDeviceGetUsedBy(other);
+ const char *other_name = NULL;
+ const char *other_drvname = NULL;
+ virPCIDeviceGetUsedBy(other, &other_drvname, &other_name);
if (other_name)
virReportError(VIR_ERR_OPERATION_INVALID,
@@ -756,7 +758,7 @@ qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver,
activeDev = virPCIDeviceListFind(driver->activePciHostdevs, dev);
if (activeDev)
- virPCIDeviceSetUsedBy(activeDev, name);
+ virPCIDeviceSetUsedBy(activeDev, "QEMU", name);
}
/* Loop 8: Now set the original states for hostdev def */
@@ -847,7 +849,9 @@ qemuPrepareHostdevUSBDevices(virQEMUDriverPtr driver,
for (i = 0; i < count; i++) {
virUSBDevicePtr usb = virUSBDeviceListGet(list, i);
if ((tmp = virUSBDeviceListFind(driver->activeUsbHostdevs, usb))) {
- const char *other_name = virUSBDeviceGetUsedBy(tmp);
+ const char *other_name = NULL;
+ const char *other_drvname = NULL;
+ virUSBDeviceGetUsedBy(tmp, &other_drvname, &other_name);
if (other_name)
virReportError(VIR_ERR_OPERATION_INVALID,
@@ -860,7 +864,7 @@ qemuPrepareHostdevUSBDevices(virQEMUDriverPtr driver,
goto error;
}
- virUSBDeviceSetUsedBy(usb, name);
+ virUSBDeviceSetUsedBy(usb, "QEMU", name);
VIR_DEBUG("Adding %03d.%03d dom=%s to activeUsbHostdevs",
virUSBDeviceGetBus(usb), virUSBDeviceGetDevno(usb),
name);
/*
@@ -1116,7 +1120,9 @@ qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr driver,
for (i = 0; i < count; i++) {
virSCSIDevicePtr scsi = virSCSIDeviceListGet(list, i);
if ((tmp = virSCSIDeviceListFind(driver->activeScsiHostdevs, scsi)))
{
- const char *other_name = virSCSIDeviceGetUsedBy(tmp);
+ const char *other_name = NULL;
+ const char *other_drvname = NULL;
+ virSCSIDeviceGetUsedBy(tmp, &other_drvname, &other_name);
if (other_name)
virReportError(VIR_ERR_OPERATION_INVALID,
@@ -1129,7 +1135,7 @@ qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr driver,
goto error;
}
- virSCSIDeviceSetUsedBy(scsi, name);
+ virSCSIDeviceSetUsedBy(scsi, "QEMU", name);
VIR_DEBUG("Adding %s to activeScsiHostdevs",
virSCSIDeviceGetName(scsi));
if (virSCSIDeviceListAdd(driver->activeScsiHostdevs, scsi) < 0)
@@ -1258,10 +1264,14 @@ qemuDomainReAttachHostdevDevices(virQEMUDriverPtr
driver,
* been used by this domain.
*/
activeDev = virPCIDeviceListFind(driver->activePciHostdevs, dev);
- if (activeDev &&
- STRNEQ_NULLABLE(name, virPCIDeviceGetUsedBy(activeDev))) {
- virPCIDeviceListDel(pcidevs, dev);
- continue;
+ if (activeDev) {
+ const char *tmp_name = NULL;
+ const char *tmp_drvname = NULL;
+ virPCIDeviceGetUsedBy(activeDev, &tmp_drvname, &tmp_name);
+ if (STRNEQ_NULLABLE(name, tmp_name)) {
+ virPCIDeviceListDel(pcidevs, dev);
+ continue;
+ }
}
virPCIDeviceListDel(driver->activePciHostdevs, dev);
@@ -1316,6 +1326,7 @@ qemuDomainReAttachHostUsbDevices(virQEMUDriverPtr
driver,
virDomainHostdevDefPtr hostdev = hostdevs[i];
virUSBDevicePtr usb, tmp;
const char *used_by = NULL;
+ const char *used_by_drvname = NULL;
if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
continue;
@@ -1353,7 +1364,7 @@ qemuDomainReAttachHostUsbDevices(virQEMUDriverPtr
driver,
continue;
}
- used_by = virUSBDeviceGetUsedBy(tmp);
+ virUSBDeviceGetUsedBy(tmp, &used_by_drvname, &used_by);
if (STREQ_NULLABLE(used_by, name)) {
VIR_DEBUG("Removing %03d.%03d dom=%s from activeUsbHostdevs",
hostdev->source.subsys.u.usb.bus,
@@ -1380,6 +1391,7 @@ qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr
driver,
virDomainHostdevDefPtr hostdev = hostdevs[i];
virSCSIDevicePtr scsi, tmp;
const char *used_by = NULL;
+ const char *used_by_drvname = NULL;
virDomainDeviceDef dev;
dev.type = VIR_DOMAIN_DEVICE_HOSTDEV;
@@ -1421,7 +1433,7 @@ qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr
driver,
continue;
}
- used_by = virSCSIDeviceGetUsedBy(tmp);
+ virSCSIDeviceGetUsedBy(tmp, &used_by_drvname, &used_by);
if (STREQ_NULLABLE(used_by, name)) {
VIR_DEBUG("Removing %s:%d:%d:%d dom=%s from
activeScsiHostdevs",
hostdev->source.subsys.u.scsi.adapter,
diff --git a/src/util/virhostdev.c b/src/util/virhostdev.c
new file mode 100644
index 0000000..63690b7
--- /dev/null
+++ b/src/util/virhostdev.c
@@ -0,0 +1,1691 @@
+/* virhostdev.c: hostdev management
+ *
+ * Copyright (C) 2013 SUSE LINUX Products GmbH, Nuernberg, Germany.
+ * Copyright (C) 2006-2007, 2009-2013 Red Hat, Inc.
+ * Copyright (C) 2006 Daniel P. Berrange
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <
http://www.gnu.org/licenses/>.
+ *
+ * Author: Chunyan Liu <cyliu(a)suse.com>
+ * Author: Daniel P. Berrange <berrange(a)redhat.com>
+ */
+
+#include <config.h>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "virhostdev.h"
+#include "viralloc.h"
+#include "virstring.h"
+#include "virfile.h"
+#include "virerror.h"
+#include "virlog.h"
+#include "virnetdev.h"
+#include "virutil.h"
+#include "configmake.h"
+
+#define VIR_FROM_THIS VIR_FROM_NONE
+
+#define HOSTDEV_STATE_DIR LOCALSTATEDIR "/run/libvirt/hostdevmgr"
+
+/* for upgrade, may need to find netconfig file in old qemu state dir */
+#define QEMU_STATE_DIR LOCALSTATEDIR "/run/libvirt/qemu"
+
+static virHostdevManagerPtr hostdevMgr;
+
+static void
+virHostdevManagerCleanup(void)
+{
+ if (!hostdevMgr)
+ return;
+
+ virObjectUnref(hostdevMgr->activePciHostdevs);
+ virObjectUnref(hostdevMgr->inactivePciHostdevs);
+ virObjectUnref(hostdevMgr->activeUsbHostdevs);
+ VIR_FREE(hostdevMgr->stateDir);
+
+ VIR_FREE(hostdevMgr);
+}
+
+static int
+virHostdevOnceInit(void)
+{
+ if (VIR_ALLOC(hostdevMgr) < 0)
+ goto error;
+
+ if ((hostdevMgr->activePciHostdevs = virPCIDeviceListNew()) == NULL)
+ goto error;
+
+ if ((hostdevMgr->activeUsbHostdevs = virUSBDeviceListNew()) == NULL)
+ goto error;
+
+ if ((hostdevMgr->inactivePciHostdevs = virPCIDeviceListNew()) == NULL)
+ goto error;
+
+ if ((hostdevMgr->activeScsiHostdevs = virSCSIDeviceListNew()) == NULL)
+ goto error;
+
+ if (VIR_STRDUP(hostdevMgr->stateDir, HOSTDEV_STATE_DIR) < 0)
+ goto error;
+
+ if (virFileMakePath(hostdevMgr->stateDir) < 0) {
+ virReportError(VIR_ERR_OPERATION_FAILED,
+ _("Failed to create state dir '%s'"),
+ hostdevMgr->stateDir);
+ goto error;
+ }
+
+ return 0;
+
+error:
+ virHostdevManagerCleanup();
+ return -1;
+}
+
+VIR_ONCE_GLOBAL_INIT(virHostdev)
+
+virHostdevManagerPtr
+virHostdevManagerGetDefault(void)
+{
+ if (virHostdevInitialize() < 0)
+ return NULL;
+ return hostdevMgr;
+}
+
+static int
+virHostdevPciSysfsPath(virDomainHostdevDefPtr hostdev, char **sysfs_path)
+{
+ virPCIDeviceAddress config_address;
+
+ config_address.domain = hostdev->source.subsys.u.pci.addr.domain;
+ config_address.bus = hostdev->source.subsys.u.pci.addr.bus;
+ config_address.slot = hostdev->source.subsys.u.pci.addr.slot;
+ config_address.function = hostdev->source.subsys.u.pci.addr.function;
+
+ return virPCIDeviceAddressGetSysfsFile(&config_address, sysfs_path);
+}
+
+static int
+virHostdevIsVirtualFunction(virDomainHostdevDefPtr hostdev)
+{
+ char *sysfs_path = NULL;
+ int ret = -1;
+
+ if (virHostdevPciSysfsPath(hostdev, &sysfs_path) < 0)
+ return ret;
+
+ ret = virPCIIsVirtualFunction(sysfs_path);
+
+ VIR_FREE(sysfs_path);
+
+ return ret;
+}
+
+static int
+virHostdevNetDevice(virDomainHostdevDefPtr hostdev,
+ char **linkdev,
+ int *vf)
+{
+ int ret = -1;
+ char *sysfs_path = NULL;
+
+ if (virHostdevPciSysfsPath(hostdev, &sysfs_path) < 0)
+ return ret;
+
+ if (virPCIIsVirtualFunction(sysfs_path) == 1) {
+ if (virPCIGetVirtualFunctionInfo(sysfs_path, linkdev, vf) < 0)
+ goto cleanup;
+ } else {
+ if (virPCIGetNetName(sysfs_path, linkdev) < 0)
+ goto cleanup;
+ *vf = -1;
+ }
+
+ ret = 0;
+
+cleanup:
+ VIR_FREE(sysfs_path);
+
+ return ret;
+}
+
+static int
+virHostdevNetConfigVirtPortProfile(const char *linkdev, int vf,
+ virNetDevVPortProfilePtr virtPort,
+ const virMacAddr *macaddr,
+ const unsigned char *uuid,
+ int associate)
+{
+ int ret = -1;
+
+ if (!virtPort)
+ return ret;
+
+ switch (virtPort->virtPortType) {
+ case VIR_NETDEV_VPORT_PROFILE_NONE:
+ case VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH:
+ case VIR_NETDEV_VPORT_PROFILE_8021QBG:
+ case VIR_NETDEV_VPORT_PROFILE_LAST:
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("virtualport type %s is "
+ "currently not supported on interfaces of type "
+ "hostdev"),
+ virNetDevVPortTypeToString(virtPort->virtPortType));
+ break;
+
+ case VIR_NETDEV_VPORT_PROFILE_8021QBH:
+ if (associate)
+ ret = virNetDevVPortProfileAssociate(NULL, virtPort, macaddr,
+ linkdev, vf, uuid,
+
VIR_NETDEV_VPORT_PROFILE_OP_CREATE, false);
+ else
+ ret = virNetDevVPortProfileDisassociate(NULL, virtPort,
+ macaddr, linkdev, vf,
+
VIR_NETDEV_VPORT_PROFILE_OP_DESTROY);
+ break;
+ }
+
+ return ret;
+}
+
+static int
+virHostdevNetConfigReplace(virDomainHostdevDefPtr hostdev,
+ const unsigned char *uuid,
+ char *stateDir)
+{
+ char *linkdev = NULL;
+ virNetDevVlanPtr vlan;
+ virNetDevVPortProfilePtr virtPort;
+ int ret = -1;
+ int vf = -1;
+ int vlanid = -1;
+ int port_profile_associate = 1;
+ int isvf;
+
+ isvf = virHostdevIsVirtualFunction(hostdev);
+ if (isvf <= 0) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("Interface type hostdev is currently supported on
"
+ "SR-IOV Virtual Functions only"));
+ return ret;
+ }
+
+ if (virHostdevNetDevice(hostdev, &linkdev, &vf) < 0)
+ return ret;
+
+ vlan = virDomainNetGetActualVlan(hostdev->parent.data.net);
+ virtPort =
virDomainNetGetActualVirtPortProfile(hostdev->parent.data.net);
+ if (virtPort) {
+ if (vlan) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("direct setting of the vlan tag is not allowed
"
+ "for hostdev devices using %s mode"),
+
virNetDevVPortTypeToString(virtPort->virtPortType));
+ goto cleanup;
+ }
+ ret = virHostdevNetConfigVirtPortProfile(linkdev, vf,
+ virtPort,
+
&hostdev->parent.data.net->mac,
+ uuid,
+ port_profile_associate);
+ } else {
+ /* Set only mac and vlan */
+ if (vlan) {
+ if (vlan->nTags != 1 || vlan->trunk) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("vlan trunking is not supported "
+ "by SR-IOV network devices"));
+ goto cleanup;
+ }
+ if (vf == -1) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("vlan can only be set for SR-IOV VFs, but "
+ "%s is not a VF"), linkdev);
+ goto cleanup;
+ }
+ vlanid = vlan->tag[0];
+ } else if (vf >= 0) {
+ vlanid = 0; /* assure any current vlan tag is reset */
+ }
+
+ ret = virNetDevReplaceNetConfig(linkdev, vf,
+ &hostdev->parent.data.net->mac,
+ vlanid, stateDir);
+ }
+cleanup:
+ VIR_FREE(linkdev);
+ return ret;
+}
+
+/* oldStateDir:
+ * For upgrade, if there is an existing VM on QEMU, the hostdev netconfig
file
+ * is previously stored in /var/run/libvirt/qemu. With new version, it
tries to
+ * find in hostdevManager->stateDir location but certainly it cannot find it.
+ * In this case, we should find in the old state dir.
+ */
+static int
+virHostdevNetConfigRestore(virDomainHostdevDefPtr hostdev,
+ char *stateDir,
+ char *oldStateDir)
+{
+ char *linkdev = NULL;
+ virNetDevVPortProfilePtr virtPort;
+ int ret = -1;
+ int vf = -1;
+ int port_profile_associate = 0;
+ int isvf;
+
+ /* This is only needed for PCI devices that have been defined
+ * using <interface type='hostdev'>. For all others, it is a NOP.
+ */
+ if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
+ hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI ||
+ hostdev->parent.type != VIR_DOMAIN_DEVICE_NET ||
+ !hostdev->parent.data.net)
+ return 0;
+
+ isvf = virHostdevIsVirtualFunction(hostdev);
+ if (isvf <= 0) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("Interface type hostdev is currently supported on
"
+ "SR-IOV Virtual Functions only"));
+ return ret;
+ }
+
+ if (virHostdevNetDevice(hostdev, &linkdev, &vf) < 0)
+ return ret;
+
+ virtPort =
virDomainNetGetActualVirtPortProfile(hostdev->parent.data.net);
+ if (virtPort)
+ ret = virHostdevNetConfigVirtPortProfile(linkdev, vf, virtPort,
+
&hostdev->parent.data.net->mac,
+ NULL,
port_profile_associate);
+ else {
+ ret = virNetDevRestoreNetConfig(linkdev, vf, stateDir);
+ if (ret < 0 && oldStateDir != NULL)
+ ret = virNetDevRestoreNetConfig(linkdev, vf, oldStateDir);
+ }
+
+ VIR_FREE(linkdev);
+
+ return ret;
+}
+
+static virPCIDeviceListPtr
+virHostdevGetPciHostDeviceList(virDomainHostdevDefPtr *hostdevs, int
nhostdevs)
+{
+ virPCIDeviceListPtr list;
+ size_t i;
+
+ if (!(list = virPCIDeviceListNew()))
+ return NULL;
+
+ for (i = 0; i < nhostdevs; i++) {
+ virDomainHostdevDefPtr hostdev = hostdevs[i];
+ virPCIDevicePtr dev;
+
+ if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
+ continue;
+ if (hostdev->source.subsys.type !=
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
+ continue;
+
+ dev = virPCIDeviceNew(hostdev->source.subsys.u.pci.addr.domain,
+ hostdev->source.subsys.u.pci.addr.bus,
+ hostdev->source.subsys.u.pci.addr.slot,
+ hostdev->source.subsys.u.pci.addr.function);
+ if (!dev) {
+ virObjectUnref(list);
+ return NULL;
+ }
+
+ if (virPCIDeviceListAdd(list, dev) < 0) {
+ virPCIDeviceFree(dev);
+ virObjectUnref(list);
+ return NULL;
+ }
+
+ virPCIDeviceSetManaged(dev, hostdev->managed);
+
+ switch (hostdev->source.subsys.u.pci.backend) {
+ case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO:
+ if (virPCIDeviceSetStubDriver(dev, "vfio-pci") < 0) {
+ virObjectUnref(list);
+ return NULL;
+ }
+ break;
+
+ case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM :
+ case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT:
+ if (virPCIDeviceSetStubDriver(dev, "pci-stub") < 0) {
+ virObjectUnref(list);
+ return NULL;
+ }
+ break;
+ }
+ }
+
+ return list;
+}
+
+bool
+virHostdevHostSupportsPassthroughVFIO(void)
+{
+ DIR *iommuDir = NULL;
+ struct dirent *iommuGroup = NULL;
+ bool ret = false;
+
+ /* condition 1 - /sys/kernel/iommu_groups/ contains entries */
+ if (!(iommuDir = opendir("/sys/kernel/iommu_groups/")))
+ goto cleanup;
+
+ while ((iommuGroup = readdir(iommuDir))) {
+ /* skip ./ ../ */
+ if (STRPREFIX(iommuGroup->d_name, "."))
+ continue;
+
+ /* assume we found a group */
+ break;
+ }
+
+ if (!iommuGroup)
+ goto cleanup;
+ /* okay, iommu is on and recognizes groups */
+
+ /* condition 2 - /dev/vfio/vfio exists */
+ if (!virFileExists("/dev/vfio/vfio"))
+ goto cleanup;
+
+ ret = true;
+
+cleanup:
+ if (iommuDir)
+ closedir(iommuDir);
+
+ return ret;
+}
+
+
+#if HAVE_LINUX_KVM_H
+# include <linux/kvm.h>
+bool
+virHostdevHostSupportsPassthroughKVM(void)
+{
+ int kvmfd = -1;
+ bool ret = false;
+
+ if ((kvmfd = open("/dev/kvm", O_RDONLY)) < 0)
+ goto cleanup;
+
+# ifdef KVM_CAP_IOMMU
+ if ((ioctl(kvmfd, KVM_CHECK_EXTENSION, KVM_CAP_IOMMU)) < 0)
Like the original code in qemu_hostdev.c, this needs to be <= 0. Otherwise, if
intel_iommu=off, the ioctl returns 0, the function returns true, and we attempt to start a
domain that is destined to fail.
I've been doing a fair bit of testing of this series and with the exception of the
above issue, haven't noticed any problems thus far. Unfortunately, you will need to
rebase the series and post a v9, but I'm confident we can get this pushed after the
1.2.1 freeze. Thanks!
Regards,
Jim
+ goto cleanup;
+
+ ret = true;
+# endif
+
+cleanup:
+ VIR_FORCE_CLOSE(kvmfd);
+
+ return ret;
+}
+#else
+bool
+virHostdevHostSupportsPassthroughKVM(void)
+{
+ return false;
+}
+#endif
+
+static bool
+virHostdevPCICheckSupport(virDomainHostdevDefPtr *hostdevs,
+ size_t nhostdevs)
+{
+ bool supportsPassthroughVFIO = virHostdevHostSupportsPassthroughVFIO();
+ bool supportsPassthroughKVM = virHostdevHostSupportsPassthroughKVM();
+ size_t i;
+
+ /* assign defaults for hostdev passthrough */
+ for (i = 0; i < nhostdevs; i++) {
+ virDomainHostdevDefPtr hostdev = hostdevs[i];
+ int *backend = &hostdev->source.subsys.u.pci.backend;
+
+ if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
+ continue;
+ if (hostdev->source.subsys.type !=
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
+ continue;
+
+ switch ((virDomainHostdevSubsysPciBackendType) *backend) {
+ case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO:
+ if (!supportsPassthroughVFIO) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("host doesn't support VFIO PCI
passthrough"));
+ return false;
+ }
+ break;
+
+ case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT:
+ case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM:
+ if (!supportsPassthroughKVM) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("host doesn't support legacy PCI
passthrough"));
+ return false;
+ }
+
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return true;
+}
+int
+virHostdevPreparePciHostdevs(virHostdevManagerPtr mgr,
+ const char *drv_name,
+ const char *dom_name,
+ const unsigned char *uuid,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs,
+ unsigned int flags)
+{
+ virPCIDeviceListPtr pcidevs = NULL;
+ int last_processed_hostdev_vf = -1;
+ size_t i;
+ int ret = -1;
+
+ if (!nhostdevs)
+ return 0;
+ if (mgr == NULL)
+ return -1;
+
+ virObjectLock(mgr->activePciHostdevs);
+ virObjectLock(mgr->inactivePciHostdevs);
+
+ if (!virHostdevPCICheckSupport(hostdevs, nhostdevs))
+ goto cleanup;
+
+ if (!(pcidevs = virHostdevGetPciHostDeviceList(hostdevs, nhostdevs)))
+ goto cleanup;
+
+ /* We have to use 9 loops here. *All* devices must
+ * be detached before we reset any of them, because
+ * in some cases you have to reset the whole PCI,
+ * which impacts all devices on it. Also, all devices
+ * must be reset before being marked as active.
+ */
+
+ /* Loop 1: validate that non-managed device isn't in use, eg
+ * by checking that device is either un-bound, or bound
+ * to stub driver
+ */
+
+ for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) {
+ virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i);
+ virPCIDevicePtr other;
+ bool strict_acs_check = !!(flags & VIR_STRICT_ACS_CHECK);
+
+ if (!virPCIDeviceIsAssignable(dev, strict_acs_check)) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("PCI device %s is not assignable"),
+ virPCIDeviceGetName(dev));
+ goto cleanup;
+ }
+ /* The device is in use by other active domain if
+ * the dev is in list activePciHostdevs.
+ */
+ if ((other = virPCIDeviceListFind(mgr->activePciHostdevs, dev))) {
+ const char *other_drvname;
+ const char *other_domname;
+
+ virPCIDeviceGetUsedBy(other, &other_drvname, &other_domname);
+ if (other_drvname && other_domname)
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("PCI device %s is in use by driver
%s,domain %s"),
+ virPCIDeviceGetName(dev), other_drvname,
+ other_domname);
+ else
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("PCI device %s is already in use"),
+ virPCIDeviceGetName(dev));
+ goto cleanup;
+ }
+ }
+
+ /* Loop 2: detach managed devices */
+ for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) {
+ virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i);
+ if (virPCIDeviceGetManaged(dev) &&
+ virPCIDeviceDetach(dev, mgr->activePciHostdevs, NULL) < 0)
+ goto reattachdevs;
+ }
+
+ /* Loop 3: Now that all the PCI hostdevs have been detached, we
+ * can safely reset them */
+ for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) {
+ virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i);
+ if (virPCIDeviceReset(dev, mgr->activePciHostdevs,
+ mgr->inactivePciHostdevs) < 0)
+ goto reattachdevs;
+ }
+
+ /* Loop 4: For SRIOV network devices, Now that we have detached the
+ * the network device, set the netdev config */
+ for (i = 0; i < nhostdevs; i++) {
+ virDomainHostdevDefPtr hostdev = hostdevs[i];
+ if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
+ continue;
+ if (hostdev->source.subsys.type !=
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
+ continue;
+ if (hostdev->parent.type == VIR_DOMAIN_DEVICE_NET &&
+
hostdev->parent.data.net) {
+ if (virHostdevNetConfigReplace(hostdev, uuid, mgr->stateDir) < 0)
+ goto resetvfnetconfig;
+ }
+ last_processed_hostdev_vf = i;
+ }
+
+ /* Loop 5: Now mark all the devices as active */
+ for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) {
+ virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i);
+ if (virPCIDeviceListAdd(mgr->activePciHostdevs, dev) < 0)
+ goto inactivedevs;
+ }
+
+ /* Loop 6: Now remove the devices from inactive list. */
+ for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) {
+ virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i);
+ virPCIDeviceListDel(mgr->inactivePciHostdevs, dev);
+ }
+
+ /* Loop 7: Now set the used_by_domain of the device in
+ * driver->activePciHostdevs as domain name.
+ */
+ for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) {
+ virPCIDevicePtr dev, activeDev;
+
+ dev = virPCIDeviceListGet(pcidevs, i);
+ activeDev = virPCIDeviceListFind(mgr->activePciHostdevs, dev);
+
+ if (activeDev)
+ virPCIDeviceSetUsedBy(activeDev, drv_name, dom_name);
+ }
+
+ /* Loop 8: Now set the original states for hostdev def */
+ for (i = 0; i < nhostdevs; i++) {
+ virPCIDevicePtr dev;
+ virPCIDevicePtr pcidev;
+ virDomainHostdevDefPtr hostdev = hostdevs[i];
+
+ if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
+ continue;
+ if (hostdev->source.subsys.type !=
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
+ continue;
+
+ dev = virPCIDeviceNew(hostdev->source.subsys.u.pci.addr.domain,
+ hostdev->source.subsys.u.pci.addr.bus,
+ hostdev->source.subsys.u.pci.addr.slot,
+ hostdev->source.subsys.u.pci.addr.function);
+
+ /* original states "unbind_from_stub", "remove_slot",
+ * "reprobe" were already set by pciDettachDevice in
+ * loop 2.
+ */
+ if ((pcidev = virPCIDeviceListFind(pcidevs, dev))) {
+ hostdev->origstates.states.pci.unbind_from_stub =
+ virPCIDeviceGetUnbindFromStub(pcidev);
+ hostdev->origstates.states.pci.remove_slot =
+ virPCIDeviceGetRemoveSlot(pcidev);
+ hostdev->origstates.states.pci.reprobe =
+ virPCIDeviceGetReprobe(pcidev);
+ }
+
+ virPCIDeviceFree(dev);
+ }
+
+ /* Loop 9: Now steal all the devices from pcidevs */
+ while (virPCIDeviceListCount(pcidevs) > 0)
+ virPCIDeviceListStealIndex(pcidevs, 0);
+
+ ret = 0;
+ goto cleanup;
+
+inactivedevs:
+ /* Only steal all the devices from driver->activePciHostdevs. We will
+ * free them in virObjectUnref().
+ */
+ while (virPCIDeviceListCount(pcidevs) > 0) {
+ virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, 0);
+ virPCIDeviceListSteal(mgr->activePciHostdevs, dev);
+ }
+
+resetvfnetconfig:
+ for (i = 0;
+ last_processed_hostdev_vf != -1 && i < last_processed_hostdev_vf;
i++)
+ virHostdevNetConfigRestore(hostdevs[i], mgr->stateDir, NULL);
+
+reattachdevs:
+ for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) {
+ virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i);
+ virPCIDeviceReattach(dev, mgr->activePciHostdevs, NULL);
+ }
+
+cleanup:
+ virObjectUnlock(mgr->activePciHostdevs);
+ virObjectUnlock(mgr->inactivePciHostdevs);
+ virObjectUnref(pcidevs);
+ return ret;
+}
+
+static int
+virHostdevFindUSBDevice(virDomainHostdevDefPtr hostdev,
+ bool mandatory,
+ virUSBDevicePtr *usb)
+{
+ unsigned vendor = hostdev->source.subsys.u.usb.vendor;
+ unsigned product = hostdev->source.subsys.u.usb.product;
+ unsigned bus = hostdev->source.subsys.u.usb.bus;
+ unsigned device = hostdev->source.subsys.u.usb.device;
+ bool autoAddress = hostdev->source.subsys.u.usb.autoAddress;
+ int rc;
+
+ *usb = NULL;
+
+ if (vendor && bus) {
+ rc = virUSBDeviceFind(vendor, product, bus, device,
+ NULL,
+ autoAddress ? false : mandatory,
+ usb);
+ if (rc < 0) {
+ return -1;
+ } else if (!autoAddress) {
+ goto out;
+ } else {
+ VIR_INFO("USB device %x:%x could not be found at previous"
+ " address (bus:%u device:%u)",
+ vendor, product, bus, device);
+ }
+ }
+
+ /* When vendor is specified, its USB address is either unspecified or
the
+ * device could not be found at the USB device where it had been
+ * automatically found before.
+ */
+ if (vendor) {
+ virUSBDeviceListPtr devs;
+
+ rc = virUSBDeviceFindByVendor(vendor, product, NULL, mandatory,
&devs);
+ if (rc < 0)
+ return -1;
+
+ if (rc == 1) {
+ *usb = virUSBDeviceListGet(devs, 0);
+ virUSBDeviceListSteal(devs, *usb);
+ }
+ virObjectUnref(devs);
+
+ if (rc == 0) {
+ goto out;
+ } else if (rc > 1) {
+ if (autoAddress) {
+ virReportError(VIR_ERR_OPERATION_FAILED,
+ _("Multiple USB devices for %x:%x were
found, "
+ "but none of them is at bus:%u
device:%u"),
+ vendor, product, bus, device);
+ } else {
+ virReportError(VIR_ERR_OPERATION_FAILED,
+ _("Multiple USB devices for %x:%x, "
+ "use <address> to specify one"),
+ vendor, product);
+ }
+ return -1;
+ }
+
+ hostdev->source.subsys.u.usb.bus = virUSBDeviceGetBus(*usb);
+ hostdev->source.subsys.u.usb.device = virUSBDeviceGetDevno(*usb);
+ hostdev->source.subsys.u.usb.autoAddress = true;
+
+ if (autoAddress) {
+ VIR_INFO("USB device %x:%x found at bus:%u device:%u (moved"
+ " from bus:%u device:%u)",
+ vendor, product,
+ hostdev->source.subsys.u.usb.bus,
+ hostdev->source.subsys.u.usb.device,
+ bus, device);
+ }
+ } else if (!vendor && bus) {
+ if (virUSBDeviceFindByBus(bus, device, NULL, mandatory, usb) < 0)
+ return -1;
+ }
+
+out:
+ if (!*usb)
+ hostdev->missing = true;
+ return 0;
+}
+
+static int
+virHostdevMarkUsbHostdevs(virHostdevManagerPtr mgr,
+ const char *drv_name,
+ const char *dom_name,
+ virUSBDeviceListPtr list)
+{
+ size_t i, j;
+ unsigned int count;
+ virUSBDevicePtr tmp;
+
+ virObjectLock(mgr->activeUsbHostdevs);
+ count = virUSBDeviceListCount(list);
+
+ for (i = 0; i < count; i++) {
+ virUSBDevicePtr usb = virUSBDeviceListGet(list, i);
+ if ((tmp = virUSBDeviceListFind(mgr->activeUsbHostdevs, usb))) {
+ const char *other_drvname;
+ const char *other_domname;
+
+ virUSBDeviceGetUsedBy(tmp, &other_drvname, &other_domname);
+ if (other_drvname && other_domname)
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("USB device %s is in use by "
+ "driver %s,domain %s"),
+ virUSBDeviceGetName(tmp),
+ other_drvname, other_domname);
+ else
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("USB device %s is already in use"),
+ virUSBDeviceGetName(tmp));
+ goto error;
+ }
+
+ virUSBDeviceSetUsedBy(usb, drv_name, dom_name);
+ VIR_DEBUG("Adding %03d.%03d driver= %s dom=%s to
activeUsbHostdevs",
+ virUSBDeviceGetBus(usb), virUSBDeviceGetDevno(usb),
+ drv_name, dom_name);
+ /*
+ * The caller is responsible to steal these usb devices
+ * from the virUSBDeviceList that passed in on success,
+ * perform rollback on failure.
+ */
+ if (virUSBDeviceListAdd(mgr->activeUsbHostdevs, usb) < 0)
+ goto error;
+ }
+
+ virObjectUnlock(mgr->activeUsbHostdevs);
+ return 0;
+
+error:
+ for (j = 0; j < i; j++) {
+ tmp = virUSBDeviceListGet(list, j);
+ virUSBDeviceListSteal(mgr->activeUsbHostdevs, tmp);
+ }
+ virObjectUnlock(mgr->activeUsbHostdevs);
+ return -1;
+}
+
+int
+virHostdevPrepareUsbHostdevs(virHostdevManagerPtr mgr,
+ const char *drv_name,
+ const char *dom_name,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs,
+ bool coldBoot)
+{
+ size_t i;
+ int ret = -1;
+ virUSBDeviceListPtr list;
+ virUSBDevicePtr tmp;
+
+ if (!nhostdevs)
+ return 0;
+ if (mgr == NULL)
+ return -1;
+
+ /* To prevent situation where USB device is assigned to two domains
+ * we need to keep a list of currently assigned USB devices.
+ * This is done in several loops which cannot be joined into one
+ * big loop.
+ */
+ if (!(list = virUSBDeviceListNew()))
+ goto cleanup;
+
+ /* Loop 1: build temporary list
+ */
+ for (i = 0; i < nhostdevs; i++) {
+ virDomainHostdevDefPtr hostdev = hostdevs[i];
+ bool required = true;
+ virUSBDevicePtr usb;
+
+ if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
+ continue;
+ if (hostdev->source.subsys.type !=
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB)
+ continue;
+
+ if (hostdev->startupPolicy == VIR_DOMAIN_STARTUP_POLICY_OPTIONAL ||
+ (hostdev->startupPolicy == VIR_DOMAIN_STARTUP_POLICY_REQUISITE
&&
+ !coldBoot))
+ required = false;
+
+ if (virHostdevFindUSBDevice(hostdev, required, &usb) < 0)
+ goto cleanup;
+
+ if (usb && virUSBDeviceListAdd(list, usb) < 0) {
+ virUSBDeviceFree(usb);
+ goto cleanup;
+ }
+ }
+
+ /* Mark devices in temporary list as used by @name
+ * and add them do active list. However, if something goes
+ * wrong, perform rollback.
+ */
+ if (virHostdevMarkUsbHostdevs(mgr, drv_name, dom_name, list) < 0)
+ goto cleanup;
+
+ /* Loop 2: Temporary list was successfully merged with
+ * active list, so steal all items to avoid freeing them
+ * in cleanup label.
+ */
+ while (virUSBDeviceListCount(list) > 0) {
+ tmp = virUSBDeviceListGet(list, 0);
+ virUSBDeviceListSteal(list, tmp);
+ }
+
+ ret = 0;
+
+cleanup:
+ virObjectUnref(list);
+ return ret;
+}
+
+int
+virHostdevPrepareScsiHostdevs(virHostdevManagerPtr mgr,
+ const char *drv_name,
+ const char *dom_name,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs)
+{
+ size_t i, j;
+ int count;
+ virSCSIDeviceListPtr list;
+ virSCSIDevicePtr tmp;
+
+ if (!nhostdevs)
+ return 0;
+ if (mgr == NULL)
+ return -1;
+
+ /* To prevent situation where SCSI device is assigned to two domains
+ * we need to keep a list of currently assigned SCSI devices.
+ */
+ if (!(list = virSCSIDeviceListNew()))
+ goto cleanup;
+
+ /* Loop 1: build temporary list */
+ for (i = 0; i < nhostdevs; i++) {
+ virDomainHostdevDefPtr hostdev = hostdevs[i];
+ virSCSIDevicePtr scsi;
+
+ if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
+ hostdev->source.subsys.type !=
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI)
+ continue;
+
+ if (hostdev->managed) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("SCSI host device doesn't support managed
mode"));
+ goto cleanup;
+ }
+
+ if (!(scsi = virSCSIDeviceNew(hostdev->source.subsys.u.scsi.adapter,
+ hostdev->source.subsys.u.scsi.bus,
+ hostdev->source.subsys.u.scsi.target,
+ hostdev->source.subsys.u.scsi.unit,
+ hostdev->readonly)))
+ goto cleanup;
+
+ if (scsi && virSCSIDeviceListAdd(list, scsi) < 0) {
+ virSCSIDeviceFree(scsi);
+ goto cleanup;
+ }
+ }
+
+ /* Loop 2: Mark devices in temporary list as used by
+ * and add them to driver list. However, if something goes
+ * wrong, perform rollback.
+ */
+ virObjectLock(mgr->activeScsiHostdevs);
+ count = virSCSIDeviceListCount(list);
+
+ for (i = 0; i < count; i++) {
+ virSCSIDevicePtr scsi = virSCSIDeviceListGet(list, i);
+ if ((tmp = virSCSIDeviceListFind(mgr->activeScsiHostdevs, scsi))) {
+ const char *other_drvname;
+ const char *other_domname;
+
+ virSCSIDeviceGetUsedBy(tmp, &other_drvname, &other_domname);
+ if (other_drvname && other_domname)
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("SCSI device %s is in use by "
+ "driver %s domain %s"),
+ virSCSIDeviceGetName(tmp),
+ other_drvname, other_domname);
+ else
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("SCSI device %s is already in use"),
+ virSCSIDeviceGetName(tmp));
+ goto error;
+ }
+
+ virSCSIDeviceSetUsedBy(scsi, drv_name, dom_name);
+ VIR_DEBUG("Adding %s to activeScsiHostdevs",
virSCSIDeviceGetName(scsi));
+
+ if (virSCSIDeviceListAdd(mgr->activeScsiHostdevs, scsi) < 0)
+ goto error;
+ }
+
+ virObjectUnlock(mgr->activeScsiHostdevs);
+
+ /* Loop 3: Temporary list was successfully merged with
+ * driver list, so steal all items to avoid freeing them
+ * when freeing temporary list.
+ */
+ while (virSCSIDeviceListCount(list) > 0) {
+ tmp = virSCSIDeviceListGet(list, 0);
+ virSCSIDeviceListSteal(list, tmp);
+ }
+
+ virObjectUnref(list);
+ return 0;
+
+error:
+ for (j = 0; j < i; j++) {
+ tmp = virSCSIDeviceListGet(list, j);
+ virSCSIDeviceListSteal(mgr->activeScsiHostdevs, tmp);
+ }
+ virObjectUnlock(mgr->activeScsiHostdevs);
+cleanup:
+ virObjectUnref(list);
+ return -1;
+}
+
+int
+virHostdevPrepareDomainHostdevs(virHostdevManagerPtr mgr,
+ const char *driver,
+ virDomainDefPtr def,
+ unsigned int flags)
+{
+ if (!def->nhostdevs)
+ return 0;
+
+ if (mgr == NULL)
+ return -1;
+
+ if (flags & VIR_SP_PCI_HOSTDEV) {
+ if (virHostdevPreparePciHostdevs(mgr, driver,
+ def->name, def->uuid,
+ def->hostdevs,
+ def->nhostdevs,
+ flags) < 0)
+ return -1;
+ }
+
+ if (flags & VIR_SP_USB_HOSTDEV) {
+ bool coldBoot = !!(flags & VIR_COLD_BOOT);
+ if (virHostdevPrepareUsbHostdevs(mgr, driver, def->name,
+ def->hostdevs, def->nhostdevs,
+ coldBoot) < 0)
+ return -1;
+ }
+
+ if (flags & VIR_SP_SCSI_HOSTDEV) {
+ if (virHostdevPrepareScsiHostdevs(mgr, driver, def->name,
+ def->hostdevs, def->nhostdevs) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Pre-condition: mgr->inactivePciHostdevs & mgr->activePciHostdevs
+ * are locked
+ */
+static void
+virHostdevReAttachPciDevice(virHostdevManagerPtr mgr, virPCIDevicePtr dev)
+{
+ /* If the device is not managed and was attached to guest
+ * successfully, it must have been inactive.
+ */
+ if (!virPCIDeviceGetManaged(dev)) {
+ if (virPCIDeviceListAdd(mgr->inactivePciHostdevs, dev) < 0)
+ virPCIDeviceFree(dev);
+ return;
+ }
+
+ /* Wait for device cleanup if it is qemu/kvm */
+ if (STREQ(virPCIDeviceGetStubDriver(dev), "pci-stub")) {
+ int retries = 100;
+ while (virPCIDeviceWaitForCleanup(dev, "kvm_assigned_device")
+ && retries) {
+ usleep(100*1000);
+ retries--;
+ }
+ }
+
+ if (virPCIDeviceReattach(dev, mgr->activePciHostdevs,
+ mgr->inactivePciHostdevs) < 0) {
+ virErrorPtr err = virGetLastError();
+ VIR_ERROR(_("Failed to re-attach PCI device: %s"),
+ err ? err->message : _("unknown error"));
+ virResetError(err);
+ }
+ virPCIDeviceFree(dev);
+}
+
+/*
+ * Pre-condition: mgr->activePciHostdevs is locked
+ */
+static virPCIDeviceListPtr
+virHostdevGetActivePciHostDeviceList(virHostdevManagerPtr mgr,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs)
+{
+ virPCIDeviceListPtr list;
+ size_t i;
+
+ if (!(list = virPCIDeviceListNew()))
+ return NULL;
+
+ for (i = 0; i < nhostdevs; i++) {
+ virDomainHostdevDefPtr hostdev = hostdevs[i];
+ virPCIDevicePtr dev;
+ virPCIDevicePtr activeDev;
+
+ if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
+ continue;
+ if (hostdev->source.subsys.type !=
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
+ continue;
+
+ dev = virPCIDeviceNew(hostdev->source.subsys.u.pci.addr.domain,
+ hostdev->source.subsys.u.pci.addr.bus,
+ hostdev->source.subsys.u.pci.addr.slot,
+ hostdev->source.subsys.u.pci.addr.function);
+ if (!dev) {
+ virObjectUnref(list);
+ return NULL;
+ }
+
+ if ((activeDev = virPCIDeviceListFind(mgr->activePciHostdevs, dev)))
{
+ if (virPCIDeviceListAdd(list, activeDev) < 0) {
+ virPCIDeviceFree(dev);
+ virObjectUnref(list);
+ return NULL;
+ }
+ }
+
+ virPCIDeviceFree(dev);
+ }
+
+ return list;
+}
+
+int
+virHostdevUpdateActivePciHostdevs(virHostdevManagerPtr mgr,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs,
+ const char *drv_name,
+ const char *dom_name)
+{
+ virDomainHostdevDefPtr hostdev = NULL;
+ virPCIDevicePtr dev = NULL;
+ size_t i;
+ int ret = -1;
+
+ if (!nhostdevs)
+ return 0;
+
+ if (!mgr)
+ return -1;
+
+ virObjectLock(mgr->activePciHostdevs);
+ virObjectLock(mgr->inactivePciHostdevs);
+
+ for (i = 0; i < nhostdevs; i++) {
+ hostdev = hostdevs[i];
+
+ if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
+ continue;
+ if (hostdev->source.subsys.type !=
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
+ continue;
+
+ dev = virPCIDeviceNew(hostdev->source.subsys.u.pci.addr.domain,
+ hostdev->source.subsys.u.pci.addr.bus,
+ hostdev->source.subsys.u.pci.addr.slot,
+ hostdev->source.subsys.u.pci.addr.function);
+
+ if (!dev)
+ goto cleanup;
+
+ virPCIDeviceSetManaged(dev, hostdev->managed);
+ switch (hostdev->source.subsys.u.pci.backend) {
+ case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO:
+ if (virPCIDeviceSetStubDriver(dev, "vfio-pci") < 0)
+ goto cleanup;
+ break;
+
+ case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM :
+ case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT:
+ if (virPCIDeviceSetStubDriver(dev, "pci-stub") < 0)
+ goto cleanup;
+ break;
+ }
+ virPCIDeviceSetUsedBy(dev, drv_name, dom_name);
+
+ /* Setup the original states for the PCI device */
+ virPCIDeviceSetUnbindFromStub(dev,
hostdev->origstates.states.pci.unbind_from_stub);
+ virPCIDeviceSetRemoveSlot(dev,
hostdev->origstates.states.pci.remove_slot);
+ virPCIDeviceSetReprobe(dev, hostdev->origstates.states.pci.reprobe);
+
+ if (virPCIDeviceListAdd(mgr->activePciHostdevs, dev) < 0)
+ goto cleanup;
+ dev = NULL;
+ }
+
+ ret = 0;
+cleanup:
+ virPCIDeviceFree(dev);
+ virObjectUnlock(mgr->activePciHostdevs);
+ virObjectUnlock(mgr->inactivePciHostdevs);
+ return ret;
+}
+
+
+int
+virHostdevUpdateActiveUsbHostdevs(virHostdevManagerPtr mgr,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs,
+ const char *drv_name,
+ const char *dom_name)
+{
+ virDomainHostdevDefPtr hostdev = NULL;
+ size_t i;
+ int ret = -1;
+
+ if (!nhostdevs)
+ return 0;
+
+ if (!mgr)
+ return -1;
+
+ virObjectLock(mgr->activeUsbHostdevs);
+ for (i = 0; i < nhostdevs; i++) {
+ virUSBDevicePtr usb = NULL;
+ hostdev = hostdevs[i];
+
+ if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
+ continue;
+ if (hostdev->source.subsys.type !=
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB)
+ continue;
+
+ usb = virUSBDeviceNew(hostdev->source.subsys.u.usb.bus,
+ hostdev->source.subsys.u.usb.device,
+ NULL);
+ if (!usb) {
+ VIR_WARN("Unable to reattach USB device %03d.%03d "
+ "on driver %s domain %s",
+ hostdev->source.subsys.u.usb.bus,
+ hostdev->source.subsys.u.usb.device,
+ drv_name, dom_name);
+ continue;
+ }
+
+ virUSBDeviceSetUsedBy(usb, drv_name, dom_name);
+
+ if (virUSBDeviceListAdd(mgr->activeUsbHostdevs, usb) < 0) {
+ virUSBDeviceFree(usb);
+ goto cleanup;
+ }
+ }
+ ret = 0;
+cleanup:
+ virObjectUnlock(mgr->activeUsbHostdevs);
+ return ret;
+}
+
+int
+virHostdevUpdateActiveScsiHostdevs(virHostdevManagerPtr mgr,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs,
+ const char *drv_name,
+ const char *dom_name)
+{
+ virDomainHostdevDefPtr hostdev = NULL;
+ size_t i;
+ int ret = -1;
+
+ if (!nhostdevs)
+ return 0;
+
+ if (!mgr)
+ return -1;
+
+ virObjectLock(mgr->activeScsiHostdevs);
+ for (i = 0; i < nhostdevs; i++) {
+ virSCSIDevicePtr scsi = NULL;
+ hostdev = hostdevs[i];
+
+ if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
+ hostdev->source.subsys.type !=
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI)
+ continue;
+
+ if (!(scsi = virSCSIDeviceNew(hostdev->source.subsys.u.scsi.adapter,
+ hostdev->source.subsys.u.scsi.bus,
+ hostdev->source.subsys.u.scsi.target,
+ hostdev->source.subsys.u.scsi.unit,
+ hostdev->readonly)))
+ goto cleanup;
+
+ virSCSIDeviceSetUsedBy(scsi, drv_name, dom_name);
+
+ if (virSCSIDeviceListAdd(mgr->activeScsiHostdevs, scsi) < 0) {
+ virSCSIDeviceFree(scsi);
+ goto cleanup;
+ }
+ }
+ ret = 0;
+
+cleanup:
+ virObjectUnlock(mgr->activeScsiHostdevs);
+ return ret;
+}
+
+int
+virHostdevUpdateActiveHostdevs(virHostdevManagerPtr mgr,
+ const char *driver,
+ virDomainDefPtr def,
+ unsigned int flags)
+{
+ if (!def->nhostdevs)
+ return 0;
+
+ if (flags & VIR_SP_PCI_HOSTDEV) {
+ if (virHostdevUpdateActivePciHostdevs(mgr,
+ def->hostdevs,
+ def->nhostdevs,
+ driver, def->name) < 0)
+ return -1;
+ }
+
+ if (flags & VIR_SP_USB_HOSTDEV) {
+ if (virHostdevUpdateActiveUsbHostdevs(mgr,
+ def->hostdevs,
+ def->nhostdevs,
+ driver, def->name) < 0)
+ return -1;
+ }
+
+ if (flags & VIR_SP_SCSI_HOSTDEV) {
+ if (virHostdevUpdateActiveScsiHostdevs(mgr,
+ def->hostdevs,
+ def->nhostdevs,
+ driver, def->name) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void
+virHostdevReAttachPciHostdevs(virHostdevManagerPtr mgr,
+ const char *drv_name,
+ const char *dom_name,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs)
+{
+ virPCIDeviceListPtr pcidevs;
+ size_t i;
+ char *oldStateDir = NULL;
+
+ if (!nhostdevs || !mgr)
+ return;
+
+ virObjectLock(mgr->activePciHostdevs);
+ virObjectLock(mgr->inactivePciHostdevs);
+
+ /* for upgrade, if netconfig file is not found in mgr->stateDir,
+ * find in old state dir */
+ if (VIR_STRDUP(oldStateDir, QEMU_STATE_DIR) < 0)
+ goto cleanup;
+
+ if (!(pcidevs = virHostdevGetActivePciHostDeviceList(mgr,
+ hostdevs,
+ nhostdevs))) {
+ virErrorPtr err = virGetLastError();
+ VIR_ERROR(_("Failed to allocate PCI device list: %s"),
+ err ? err->message : _("unknown error"));
+ virResetError(err);
+ goto cleanup;
+ }
+
+ /* Again 4 loops; mark all devices as inactive before reset
+ * them and reset all the devices before re-attach.
+ * Attach mac and port profile parameters to devices
+ */
+ for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) {
+ virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i);
+ virPCIDevicePtr activeDev = NULL;
+ const char *usedby_drvname;
+ const char *usedby_domname;
+
+ /* Never delete the dev from list driver->activePciHostdevs
+ * if it's used by other domain.
+ */
+ activeDev = virPCIDeviceListFind(mgr->activePciHostdevs, dev);
+ if (activeDev) {
+ virPCIDeviceGetUsedBy(activeDev, &usedby_drvname,
&usedby_domname);
+ if (STRNEQ_NULLABLE(drv_name, usedby_drvname) ||
+ STRNEQ_NULLABLE(dom_name, usedby_domname)) {
+ virPCIDeviceListSteal(pcidevs, dev);
+ continue;
+ }
+ }
+
+ virPCIDeviceListSteal(mgr->activePciHostdevs, dev);
+ }
+
+ /*
+ * For SRIOV net host devices, unset mac and port profile before
+ * reset and reattach device
+ */
+ for (i = 0; i < nhostdevs; i++)
+ virHostdevNetConfigRestore(hostdevs[i], mgr->stateDir, oldStateDir);
+
+ for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) {
+ virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i);
+ if (virPCIDeviceReset(dev, mgr->activePciHostdevs,
+ mgr->inactivePciHostdevs) < 0) {
+ virErrorPtr err = virGetLastError();
+ VIR_ERROR(_("Failed to reset PCI device: %s"),
+ err ? err->message : _("unknown error"));
+ virResetError(err);
+ }
+ }
+
+ while (virPCIDeviceListCount(pcidevs) > 0) {
+ virPCIDevicePtr dev = virPCIDeviceListStealIndex(pcidevs, 0);
+ virHostdevReAttachPciDevice(mgr, dev);
+ }
+
+ virObjectUnref(pcidevs);
+cleanup:
+ VIR_FREE(oldStateDir);
+ virObjectUnlock(mgr->activePciHostdevs);
+ virObjectUnlock(mgr->inactivePciHostdevs);
+}
+
+void
+virHostdevReAttachUsbHostdevs(virHostdevManagerPtr mgr,
+ const char *drv_name,
+ const char *dom_name,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs)
+{
+ size_t i;
+
+ if (!nhostdevs || !mgr)
+ return;
+
+ virObjectLock(mgr->activeUsbHostdevs);
+ for (i = 0; i < nhostdevs; i++) {
+ virDomainHostdevDefPtr hostdev = hostdevs[i];
+ virUSBDevicePtr usb, tmp;
+ const char *usedby_drvname;
+ const char *usedby_domname;
+
+ if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
+ continue;
+ if (hostdev->source.subsys.type !=
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB)
+ continue;
+ if (hostdev->missing)
+ continue;
+
+ usb = virUSBDeviceNew(hostdev->source.subsys.u.usb.bus,
+ hostdev->source.subsys.u.usb.device,
+ NULL);
+
+ if (!usb) {
+ VIR_WARN("Unable to reattach USB device %03d.%03d "
+ "on driver %s domain %s",
+ hostdev->source.subsys.u.usb.bus,
+ hostdev->source.subsys.u.usb.device,
+ drv_name, dom_name);
+ continue;
+ }
+
+ /* Delete only those USB devices which belongs
+ * to domain. Therefore we want to steal only
+ * those devices from the list which were taken
+ * by domain */
+
+ tmp = virUSBDeviceListFind(mgr->activeUsbHostdevs, usb);
+ virUSBDeviceFree(usb);
+
+ if (!tmp) {
+ VIR_WARN("Unable to find device %03d.%03d "
+ "in list of active USB devices",
+ hostdev->source.subsys.u.usb.bus,
+ hostdev->source.subsys.u.usb.device);
+ continue;
+ }
+
+ virUSBDeviceGetUsedBy(tmp, &usedby_drvname, &usedby_domname);
+ if (STREQ_NULLABLE(drv_name, usedby_drvname) &&
+ STREQ_NULLABLE(dom_name, usedby_domname)) {
+ VIR_DEBUG("Removing %03d.%03d dom=%s:%s",
+ hostdev->source.subsys.u.usb.bus,
+ hostdev->source.subsys.u.usb.device,
+ drv_name, dom_name);
+ virUSBDeviceListDel(mgr->activeUsbHostdevs, tmp);
+ }
+
+ }
+
+ virObjectUnlock(mgr->activeUsbHostdevs);
+}
+
+void
+virHostdevReAttachScsiHostdevs(virHostdevManagerPtr mgr,
+ const char *drv_name,
+ const char *dom_name,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs)
+{
+ size_t i;
+
+ if (!nhostdevs || !mgr)
+ return;
+
+ virObjectLock(mgr->activeScsiHostdevs);
+ for (i = 0; i < nhostdevs; i++) {
+ virDomainHostdevDefPtr hostdev = hostdevs[i];
+ virSCSIDevicePtr scsi, tmp;
+ const char *usedby_drvname;
+ const char *usedby_domname;
+
+ if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
+ hostdev->source.subsys.type !=
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI)
+ continue;
+
+ if (!(scsi = virSCSIDeviceNew(hostdev->source.subsys.u.scsi.adapter,
+ hostdev->source.subsys.u.scsi.bus,
+ hostdev->source.subsys.u.scsi.target,
+ hostdev->source.subsys.u.scsi.unit,
+ hostdev->readonly))) {
+ VIR_WARN("Unable to reattach SCSI device %s:%d:%d:%d on driver
%s domain %s",
+ hostdev->source.subsys.u.scsi.adapter,
+ hostdev->source.subsys.u.scsi.bus,
+ hostdev->source.subsys.u.scsi.target,
+ hostdev->source.subsys.u.scsi.unit,
+ drv_name, dom_name);
+ continue;
+ }
+
+ tmp = virSCSIDeviceListFind(mgr->activeScsiHostdevs, scsi);
+ virSCSIDeviceFree(scsi);
+
+ if (!tmp) {
+ VIR_WARN("Unable to find device %s:%d:%d:%d "
+ "in list of active SCSI devices",
+ hostdev->source.subsys.u.scsi.adapter,
+ hostdev->source.subsys.u.scsi.bus,
+ hostdev->source.subsys.u.scsi.target,
+ hostdev->source.subsys.u.scsi.unit);
+ continue;
+ }
+
+ virSCSIDeviceGetUsedBy(tmp, &usedby_drvname, &usedby_domname);
+ if (STREQ_NULLABLE(usedby_drvname, drv_name) &&
+ STREQ_NULLABLE(usedby_domname, dom_name)) {
+ VIR_DEBUG("Removing %s:%d:%d:%d driver:%s dom:%s from
activeScsiHostdevs",
+ hostdev->source.subsys.u.scsi.adapter,
+ hostdev->source.subsys.u.scsi.bus,
+ hostdev->source.subsys.u.scsi.target,
+ hostdev->source.subsys.u.scsi.unit,
+ drv_name, dom_name);
+ virSCSIDeviceListDel(mgr->activeScsiHostdevs, tmp);
+ }
+ }
+
+ virObjectUnlock(mgr->activeScsiHostdevs);
+}
+
+void
+virHostdevReAttachDomainHostdevs(virHostdevManagerPtr mgr,
+ const char *driver,
+ virDomainDefPtr def,
+ unsigned int flags)
+{
+ if (!def->nhostdevs || !mgr)
+ return;
+
+ if (flags & VIR_SP_PCI_HOSTDEV) {
+ virHostdevReAttachPciHostdevs(mgr, driver, def->name,
+ def->hostdevs, def->nhostdevs);
+ }
+
+ if (flags & VIR_SP_USB_HOSTDEV) {
+ virHostdevReAttachUsbHostdevs(mgr, driver, def->name,
+ def->hostdevs, def->nhostdevs);
+ }
+
+ if (flags & VIR_SP_SCSI_HOSTDEV) {
+ virHostdevReAttachScsiHostdevs(mgr, driver, def->name,
+ def->hostdevs, def->nhostdevs);
+ }
+}
+
+/* following functions are used by NodeDevDetach/Reattach/Reset */
+int
+virHostdevPciNodeDeviceDetach(virHostdevManagerPtr mgr,
+ virPCIDevicePtr pci)
+{
+ int ret;
+
+ if (!mgr || !pci)
+ return -1;
+
+ virObjectLock(mgr->activePciHostdevs);
+ virObjectLock(mgr->inactivePciHostdevs);
+ if (virPCIDeviceDetach(pci, mgr->activePciHostdevs,
+ mgr->inactivePciHostdevs) < 0)
+ ret = -1;
+ else
+ ret = 0;
+
+ virObjectUnlock(mgr->inactivePciHostdevs);
+ virObjectUnlock(mgr->activePciHostdevs);
+ return ret;
+}
+
+int
+virHostdevPciNodeDeviceReAttach(virHostdevManagerPtr mgr,
+ virPCIDevicePtr pci)
+{
+ int ret = -1;
+ virPCIDevicePtr other;
+
+ if (!mgr || !pci)
+ return -1;
+
+ virObjectLock(mgr->activePciHostdevs);
+ virObjectLock(mgr->inactivePciHostdevs);
+ other = virPCIDeviceListFind(mgr->activePciHostdevs, pci);
+ if (other) {
+ const char *other_drvname;
+ const char *other_domname;
+
+ virPCIDeviceGetUsedBy(other, &other_drvname, &other_domname);
+ if (other_drvname && other_domname)
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("PCI device %s is still in use by driver %s,
domain %s"),
+ virPCIDeviceGetName(pci), other_drvname,
other_domname);
+ else
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("PCI device %s is still in use"),
+ virPCIDeviceGetName(pci));
+ goto out;
+ }
+
+ virPCIDeviceReattachInit(pci);
+
+ if (virPCIDeviceReattach(pci, mgr->activePciHostdevs,
+ mgr->inactivePciHostdevs) < 0)
+ goto out;
+
+ ret = 0;
+out:
+ virObjectUnlock(mgr->inactivePciHostdevs);
+ virObjectUnlock(mgr->activePciHostdevs);
+ return ret;
+}
+
+int
+virHostdevPciNodeDeviceReset(virHostdevManagerPtr mgr,
+ virPCIDevicePtr pci)
+{
+ int ret;
+
+ if (!mgr || !pci)
+ return -1;
+
+ virObjectLock(mgr->activePciHostdevs);
+ virObjectLock(mgr->inactivePciHostdevs);
+ if (virPCIDeviceReset(pci, mgr->activePciHostdevs,
+ mgr->inactivePciHostdevs) < 0)
+ ret = -1;
+ else
+ ret = 0;
+
+ virObjectUnlock(mgr->inactivePciHostdevs);
+ virObjectUnlock(mgr->activePciHostdevs);
+ return ret;
+}
diff --git a/src/util/virhostdev.h b/src/util/virhostdev.h
new file mode 100644
index 0000000..fc69540
--- /dev/null
+++ b/src/util/virhostdev.h
@@ -0,0 +1,134 @@
+/* virhostdev.h: hostdev management
+ *
+ * Copyright (C) 2013 SUSE LINUX Products GmbH, Nuernberg, Germany.
+ * Copyright (C) 2006-2007, 2009-2013 Red Hat, Inc.
+ * Copyright (C) 2006 Daniel P. Berrange
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <
http://www.gnu.org/licenses/>.
+ *
+ * Author: Chunyan Liu <cyliu(a)suse.com>
+ * Author: Daniel P. Berrange <berrange(a)redhat.com>
+ */
+
+#ifndef __VIR_HOSTDEV_H__
+# define __VIR_HOSTDEV_H__
+
+# include "internal.h"
+
+# include "domain_conf.h"
+# include "virpci.h"
+# include "virusb.h"
+# include "virscsi.h"
+
+typedef enum {
+ VIR_SP_PCI_HOSTDEV = (1 << 0), /* support pci passthrough */
+ VIR_SP_USB_HOSTDEV = (1 << 1), /* support usb passthrough */
+ VIR_SP_SCSI_HOSTDEV = (1 << 2), /* support scsi passthrough */
+
+ VIR_COLD_BOOT = (1 << 8), /* cold boot */
+ VIR_STRICT_ACS_CHECK = (1 << 9), /* strict acs check */
+} virHostdevManagerFlag;
+
+typedef struct _virHostdevManager virHostdevManager;
+typedef virHostdevManager *virHostdevManagerPtr;
+struct _virHostdevManager{
+ char *stateDir;
+
+ virPCIDeviceListPtr activePciHostdevs;
+ virPCIDeviceListPtr inactivePciHostdevs;
+ virUSBDeviceListPtr activeUsbHostdevs;
+ virSCSIDeviceListPtr activeScsiHostdevs;
+};
+
+virHostdevManagerPtr virHostdevManagerGetDefault(void);
+
+bool virHostdevHostSupportsPassthroughVFIO(void);
+bool virHostdevHostSupportsPassthroughKVM(void);
+
+/* functions used to prepare/unprepare hostdevs for domain */
+int virHostdevPrepareDomainHostdevs(virHostdevManagerPtr mgr,
+ const char *driver,
+ virDomainDefPtr def,
+ unsigned int flags);
+void virHostdevReAttachDomainHostdevs(virHostdevManagerPtr mgr,
+ const char *driver,
+ virDomainDefPtr def,
+ unsigned int flags);
+int virHostdevPreparePciHostdevs(virHostdevManagerPtr mgr,
+ const char *drv_name,
+ const char *dom_name,
+ const unsigned char *uuid,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs,
+ unsigned int flags);
+int virHostdevPrepareUsbHostdevs(virHostdevManagerPtr mgr,
+ const char *drv_name,
+ const char *dom_name,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs,
+ bool coldBoot);
+int virHostdevPrepareScsiHostdevs(virHostdevManagerPtr mgr,
+ const char *drv_name,
+ const char *dom_name,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs);
+void virHostdevReAttachPciHostdevs(virHostdevManagerPtr mgr,
+ const char *drv_name,
+ const char *dom_name,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs);
+void virHostdevReAttachUsbHostdevs(virHostdevManagerPtr mgr,
+ const char *drv_name,
+ const char *dom_name,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs);
+void virHostdevReAttachScsiHostdevs(virHostdevManagerPtr mgr,
+ const char *drv_name,
+ const char *dom_name,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs);
+int
+virHostdevUpdateActivePciHostdevs(virHostdevManagerPtr mgr,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs,
+ const char *drv_name,
+ const char *dom_name);
+int
+virHostdevUpdateActiveUsbHostdevs(virHostdevManagerPtr mgr,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs,
+ const char *drv_name,
+ const char *dom_name);
+int
+virHostdevUpdateActiveScsiHostdevs(virHostdevManagerPtr mgr,
+ virDomainHostdevDefPtr *hostdevs,
+ int nhostdevs,
+ const char *drv_name,
+ const char *dom_name);
+int
+virHostdevUpdateActiveHostdevs(virHostdevManagerPtr mgr,
+ const char *driver,
+ virDomainDefPtr def,
+ unsigned int flags);
+
+/* functions used by NodeDevDetach/Reattach/Reset */
+int virHostdevPciNodeDeviceDetach(virHostdevManagerPtr mgr,
+ virPCIDevicePtr pci);
+int virHostdevPciNodeDeviceReAttach(virHostdevManagerPtr mgr,
+ virPCIDevicePtr pci);
+int virHostdevPciNodeDeviceReset(virHostdevManagerPtr mgr,
+ virPCIDevicePtr pci);
+
+#endif /* __VIR_HOSTDEV_H__ */
diff --git a/src/util/virpci.c b/src/util/virpci.c
index 8ec642f..7402898 100644
--- a/src/util/virpci.c
+++ b/src/util/virpci.c
@@ -58,7 +58,10 @@ struct _virPCIDevice {
char name[PCI_ADDR_LEN]; /* domain:bus:slot.function */
char id[PCI_ID_LEN]; /* product vendor */
char *path;
- const char *used_by; /* The domain which uses the device */
+
+ /* The driver:domain which uses the device */
+ char *used_by_drvname;
+ char *used_by_domname;
unsigned int pcie_cap_pos;
unsigned int pci_pm_cap_pos;
@@ -1562,6 +1565,8 @@ virPCIDeviceFree(virPCIDevicePtr dev)
VIR_DEBUG("%s %s: freeing", dev->id, dev->name);
VIR_FREE(dev->path);
VIR_FREE(dev->stubDriver);
+ VIR_FREE(dev->used_by_drvname);
+ VIR_FREE(dev->used_by_domname);
VIR_FREE(dev);
}
@@ -1631,16 +1636,27 @@ virPCIDeviceSetReprobe(virPCIDevicePtr dev, bool
reprobe)
dev->reprobe = reprobe;
}
-void
-virPCIDeviceSetUsedBy(virPCIDevicePtr dev, const char *name)
+int
+virPCIDeviceSetUsedBy(virPCIDevicePtr dev,
+ const char *drv_name,
+ const char *dom_name)
{
- dev->used_by = name;
+ if (VIR_STRDUP(dev->used_by_drvname, drv_name) < 0)
+ return -1;
+
+ if (VIR_STRDUP(dev->used_by_domname, dom_name) < 0)
+ return -1;
+
+ return 0;
}
-const char *
-virPCIDeviceGetUsedBy(virPCIDevicePtr dev)
+void
+virPCIDeviceGetUsedBy(virPCIDevicePtr dev,
+ const char **drv_name,
+ const char **dom_name)
{
- return dev->used_by;
+ *drv_name = dev->used_by_drvname;
+ *dom_name = dev->used_by_domname;
}
void virPCIDeviceReattachInit(virPCIDevicePtr pci)
diff --git a/src/util/virpci.h b/src/util/virpci.h
index 0479f0b..2347b95 100644
--- a/src/util/virpci.h
+++ b/src/util/virpci.h
@@ -65,9 +65,12 @@ unsigned int virPCIDeviceGetManaged(virPCIDevice *dev);
int virPCIDeviceSetStubDriver(virPCIDevicePtr dev,
const char *driver);
const char *virPCIDeviceGetStubDriver(virPCIDevicePtr dev);
-void virPCIDeviceSetUsedBy(virPCIDevice *dev,
- const char *used_by);
-const char *virPCIDeviceGetUsedBy(virPCIDevice *dev);
+int virPCIDeviceSetUsedBy(virPCIDevice *dev,
+ const char *drv_name,
+ const char *dom_name);
+void virPCIDeviceGetUsedBy(virPCIDevice *dev,
+ const char **drv_name,
+ const char **dom_name);
unsigned int virPCIDeviceGetUnbindFromStub(virPCIDevicePtr dev);
void virPCIDeviceSetUnbindFromStub(virPCIDevice *dev,
bool unbind);
diff --git a/src/util/virscsi.c b/src/util/virscsi.c
index 7aca9e6..062e8bd 100644
--- a/src/util/virscsi.c
+++ b/src/util/virscsi.c
@@ -55,7 +55,10 @@ struct _virSCSIDevice {
char *name; /* adapter:bus:target:unit */
char *id; /* model:vendor */
char *sg_path; /* e.g. /dev/sg2 */
- const char *used_by; /* name of the domain using this dev */
+
+ /* driver:domain using this dev */
+ char *used_by_drvname;
+ char *used_by_domname;
bool readonly;
};
@@ -259,20 +262,31 @@ virSCSIDeviceFree(virSCSIDevicePtr dev)
VIR_FREE(dev->id);
VIR_FREE(dev->name);
VIR_FREE(dev->sg_path);
+ VIR_FREE(dev->used_by_drvname);
+ VIR_FREE(dev->used_by_domname);
VIR_FREE(dev);
}
-void
+int
virSCSIDeviceSetUsedBy(virSCSIDevicePtr dev,
- const char *name)
+ const char *drvname,
+ const char *domname)
{
- dev->used_by = name;
+ if (VIR_STRDUP(dev->used_by_drvname, drvname) < 0)
+ return -1;
+ if (VIR_STRDUP(dev->used_by_domname, domname) < 0)
+ return -1;
+
+ return 0;
}
-const char *
-virSCSIDeviceGetUsedBy(virSCSIDevicePtr dev)
+void
+virSCSIDeviceGetUsedBy(virSCSIDevicePtr dev,
+ const char **drvname,
+ const char **domname)
{
- return dev->used_by;
+ *drvname = dev->used_by_drvname;
+ *domname = dev->used_by_domname;
}
const char *
diff --git a/src/util/virscsi.h b/src/util/virscsi.h
index cce5df4..263b175 100644
--- a/src/util/virscsi.h
+++ b/src/util/virscsi.h
@@ -49,8 +49,12 @@ virSCSIDevicePtr virSCSIDeviceNew(const char *adapter,
bool readonly);
void virSCSIDeviceFree(virSCSIDevicePtr dev);
-void virSCSIDeviceSetUsedBy(virSCSIDevicePtr dev, const char *name);
-const char *virSCSIDeviceGetUsedBy(virSCSIDevicePtr dev);
+int virSCSIDeviceSetUsedBy(virSCSIDevicePtr dev,
+ const char *drvname,
+ const char *domname);
+void virSCSIDeviceGetUsedBy(virSCSIDevicePtr dev,
+ const char **drvname,
+ const char **domname);
const char *virSCSIDeviceGetName(virSCSIDevicePtr dev);
unsigned int virSCSIDeviceGetAdapter(virSCSIDevicePtr dev);
unsigned int virSCSIDeviceGetBus(virSCSIDevicePtr dev);
diff --git a/src/util/virusb.c b/src/util/virusb.c
index 3c82200..7b17b0a 100644
--- a/src/util/virusb.c
+++ b/src/util/virusb.c
@@ -55,7 +55,10 @@ struct _virUSBDevice {
char name[USB_ADDR_LEN]; /* domain:bus:slot.function */
char id[USB_ID_LEN]; /* product vendor */
char *path;
- const char *used_by; /* name of the domain using this dev
*/
+
+ /* driver:domain using this dev */
+ char *used_by_drvname;
+ char *used_by_domname;
};
struct _virUSBDeviceList {
@@ -375,19 +378,31 @@ virUSBDeviceFree(virUSBDevicePtr dev)
return;
VIR_DEBUG("%s %s: freeing", dev->id, dev->name);
VIR_FREE(dev->path);
+ VIR_FREE(dev->used_by_drvname);
+ VIR_FREE(dev->used_by_domname);
VIR_FREE(dev);
}
-
-void virUSBDeviceSetUsedBy(virUSBDevicePtr dev,
- const char *name)
+int
+virUSBDeviceSetUsedBy(virUSBDevicePtr dev,
+ const char *drv_name,
+ const char *dom_name)
{
- dev->used_by = name;
+ if (VIR_STRDUP(dev->used_by_drvname, drv_name) < 0)
+ return -1;
+ if (VIR_STRDUP(dev->used_by_domname, dom_name) < 0)
+ return -1;
+
+ return 0;
}
-const char * virUSBDeviceGetUsedBy(virUSBDevicePtr dev)
+void
+virUSBDeviceGetUsedBy(virUSBDevicePtr dev,
+ const char **drv_name,
+ const char **dom_name)
{
- return dev->used_by;
+ *drv_name = dev->used_by_drvname;
+ *dom_name = dev->used_by_domname;
}
const char *virUSBDeviceGetName(virUSBDevicePtr dev)
diff --git a/src/util/virusb.h b/src/util/virusb.h
index aa59d12..41e680f 100644
--- a/src/util/virusb.h
+++ b/src/util/virusb.h
@@ -60,8 +60,12 @@ int virUSBDeviceFind(unsigned int vendor,
virUSBDevicePtr *usb);
void virUSBDeviceFree(virUSBDevicePtr dev);
-void virUSBDeviceSetUsedBy(virUSBDevicePtr dev, const char *name);
-const char *virUSBDeviceGetUsedBy(virUSBDevicePtr dev);
+int virUSBDeviceSetUsedBy(virUSBDevicePtr dev,
+ const char *drv_name,
+ const char *dom_name);
+void virUSBDeviceGetUsedBy(virUSBDevicePtr dev,
+ const char **drv_name,
+ const char **dom_name);
const char *virUSBDeviceGetName(virUSBDevicePtr dev);
unsigned int virUSBDeviceGetBus(virUSBDevicePtr dev);