[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[libvirt] [v11 1/6] add hostdev passthrough common library



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 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 suse com>
+ * Author: Daniel P. Berrange <berrange 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)
+        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,
+                                      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;
+        }
+
+        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,
+                                      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);
+    }
+
+    /*
+     * 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,
+                                      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 suse com>
+ * Author: Daniel P. Berrange <berrange 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);
-- 
1.6.0.2


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]