Hello ChunYan,
When trying to rebase your patchset, I found a few bits of missing code
from the qemu_hostdev.c. It would be good if you could have a look at
those.
On Fri, 2014-01-24 at 00:47 +0800, Chunyan Liu wrote:
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 | 1694 ++++++++++++++++++++++++++++++++++++++++++++++
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, 1972 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 0359b2f..60c226a 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -160,6 +160,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 7844efa..5ab8d86 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 13caf93..0cb877c 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1272,6 +1272,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 6e21267..dcc76a0 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -11273,7 +11273,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 86a463a..16e4c44 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);
@@ -271,7 +271,7 @@ qemuUpdateActiveScsiHostdevs(virQEMUDriverPtr driver,
hostdev->shareable)))
goto cleanup;
- virSCSIDeviceSetUsedBy(scsi, def->name);
+ virSCSIDeviceSetUsedBy(scsi, "QEMU", def->name);
if (virSCSIDeviceListAdd(driver->activeScsiHostdevs, scsi) < 0) {
virSCSIDeviceFree(scsi);
@@ -684,7 +684,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,
@@ -757,7 +759,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 */
@@ -848,7 +850,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,
@@ -861,7 +865,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);
/*
@@ -1118,7 +1122,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,
@@ -1131,7 +1137,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)
@@ -1260,10 +1266,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);
@@ -1318,6 +1328,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;
@@ -1355,7 +1366,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,
@@ -1382,6 +1393,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;
@@ -1424,7 +1436,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..fa178e2
--- /dev/null
+++ b/src/util/virhostdev.c
@@ -0,0 +1,1694 @@
+/* virhostdev.c: hostdev management
+ *
+ * Copyright (C) 2014 SUSE LINUX Products GmbH, Nuernberg, Germany.
+ * Copyright (C) 2006-2007, 2009-2014 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)
Commit 5debc722 changed that two-state int and a few others to bool.
+{
+ 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;
Again int changed to bool by commit 5debc722.
+ 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)
+ 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:
Actually, default has been implemented by commit f094aaac. But it's
QEMU-specific code that needs to remain after the switch to this new
code. Sadly, the missing code isn't in the patch 3/6.
+ 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;
Is there any reason to invert the lock and the CheckSupport?
+ 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);
We lost a comment here, is that intentional?
+ 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,
Is there any reason not to keep virDomainDefPtr as param like in the
original qemu function?
+ 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;
+
There is a first loop missing here that was added by commit 1d94b3e7.
This loop is qemu-only code, but I can't see any equivalent for it after
the switch to the common hostdev code in patch 3/6.
+ /* 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,
+ hostdev->shareable)))
+ 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;
+ }
+
This code has been changed with commit b2a2d00f. Would be good to sync
the two files.
+ 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)
Again, is there any rationale for splitting the virDomainDefPtr from the
arguments into hostdevs, nhostdevs and 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)
Same question about virDomainDefPtr
+{
+ 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)
Same question about virDomainDefPtr
+{
+ 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,
+ hostdev->shareable)))
+ 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);
Here and a few lines above, commit b2a2d00f replaced
virPCIDeviceListSteal by virPCIDeviceListDel.
+ }
+
Missing one comment here
> + /*
> + * 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;
> +
Some code from commit 1d94b3e7 is missing here. It may be qemu-only
code, but I can't see it anywhere in patch 3/6 after the removal.
--
Cedric
> + 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,
> + hostdev->shareable))) {
> + 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..e15a70d
> --- /dev/null
> +++ b/src/util/virhostdev.h
> @@ -0,0 +1,134 @@
> +/* virhostdev.h: hostdev management
> + *
> + * Copyright (C) 2014 SUSE LINUX Products GmbH, Nuernberg, Germany.
> + * Copyright (C) 2006-2007, 2009-2014 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 e2d222e..bc97fa5 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;
> @@ -1622,6 +1625,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);
> }
>
> @@ -1691,16 +1696,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 42c3c95..b0ffa64 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 58d9e25..15df041 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;
> bool shareable;
> @@ -262,20 +265,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 b2e98ca..d2857a5 100644
> --- a/src/util/virscsi.h
> +++ b/src/util/virscsi.h
> @@ -50,8 +50,12 @@ virSCSIDevicePtr virSCSIDeviceNew(const char *adapter,
> bool shareable);
>
> 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 bb5980d..4e22973 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 e0a7c4c..f98ea21 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);