Blob Blame History Raw
From 89eefbd116ae74c3a5cfcfc74a31a40b83c726c3 Mon Sep 17 00:00:00 2001
From: Mark McLoughlin <markmc@redhat.com>
Date: Mon, 17 Aug 2009 15:05:23 +0100
Subject: [PATCH] Maintain a list of active PCI hostdevs and use it in pciResetDevice()

https://bugzilla.redhat.com/499678

First we add a pciDeviceList type and add a qemuGetPciHostDeviceList()
function to build a list from a domain definition. Use this in
prepare/re-attach to simplify things and eliminate the multiple
pciGetDevice() calls.

Then, as we start/shutdown guests we can add or delete devices as
appropriate from a list of active devices.

Finally, in pciReset(), we can use this to determine whether its safe to
reset a device as a side effect of resetting another device.

(cherry picked from commit 78675b228b76a83f83d64856bfb63b9e14c103a0)
(cherry picked from commit e8ad33931296c67de0538e78d12e21706a826d37)

Fedora-patch: libvirt-allow-pci-hostdev-reset-to-reset-other-devices.patch
---
 src/libvirt_private.syms |    7 +-
 src/pci.c                |  211 +++++++++++++++++++++++++++++++++--------
 src/pci.h                |   23 +++++-
 src/qemu_conf.h          |    3 +
 src/qemu_driver.c        |  237 +++++++++++++++++++++++++++-------------------
 src/xen_unified.c        |    2 +-
 6 files changed, 339 insertions(+), 144 deletions(-)

diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index bd63692..4f1b01f 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -278,7 +278,12 @@ pciFreeDevice;
 pciDettachDevice;
 pciReAttachDevice;
 pciResetDevice;
-
+pciDeviceSetManaged;
+pciDeviceGetManaged;
+pciDeviceListNew;
+pciDeviceListFree;
+pciDeviceListAdd;
+pciDeviceListDel;
 
 # qparams.h
 qparam_get_query;
diff --git a/src/pci.c b/src/pci.c
index 74f7ef0..96e5d6d 100644
--- a/src/pci.c
+++ b/src/pci.c
@@ -63,6 +63,7 @@ struct _pciDevice {
     unsigned      pci_pm_cap_pos;
     unsigned      has_flr : 1;
     unsigned      has_pm_reset : 1;
+    unsigned      managed : 1;
 };
 
 /* For virReportOOMError()  and virReportSystemError() */
@@ -225,7 +226,7 @@ pciWrite32(pciDevice *dev, unsigned pos, uint32_t val)
     pciWrite(dev, pos, &buf[0], sizeof(buf));
 }
 
-typedef int (*pciIterPredicate)(pciDevice *, pciDevice *);
+typedef int (*pciIterPredicate)(pciDevice *, pciDevice *, void *);
 
 /* Iterate over available PCI devices calling @predicate
  * to compare each one to @dev.
@@ -236,7 +237,8 @@ static int
 pciIterDevices(virConnectPtr conn,
                pciIterPredicate predicate,
                pciDevice *dev,
-               pciDevice **matched)
+               pciDevice **matched,
+               void *data)
 {
     DIR *dir;
     struct dirent *entry;
@@ -254,7 +256,7 @@ pciIterDevices(virConnectPtr conn,
 
     while ((entry = readdir(dir))) {
         unsigned domain, bus, slot, function;
-        pciDevice *try;
+        pciDevice *check;
 
         /* Ignore '.' and '..' */
         if (entry->d_name[0] == '.')
@@ -266,18 +268,18 @@ pciIterDevices(virConnectPtr conn,
             continue;
         }
 
-        try = pciGetDevice(conn, domain, bus, slot, function);
-        if (!try) {
+        check = pciGetDevice(conn, domain, bus, slot, function);
+        if (!check) {
             ret = -1;
             break;
         }
 
-        if (predicate(try, dev)) {
-            VIR_DEBUG("%s %s: iter matched on %s", dev->id, dev->name, try->name);
-            *matched = try;
+        if (predicate(dev, check, data)) {
+            VIR_DEBUG("%s %s: iter matched on %s", dev->id, dev->name, check->name);
+            *matched = check;
             break;
         }
-        pciFreeDevice(conn, try);
+        pciFreeDevice(conn, check);
     }
     closedir(dir);
     return ret;
@@ -379,63 +381,70 @@ pciDetectPowerManagementReset(pciDevice *dev)
     return 0;
 }
 
-/* Any devices other than the one supplied on the same domain/bus ? */
+/* Any active devices other than the one supplied on the same domain/bus ? */
 static int
-pciSharesBus(pciDevice *a, pciDevice *b)
+pciSharesBusWithActive(pciDevice *dev, pciDevice *check, void *data)
 {
-    return
-        a->domain == b->domain &&
-        a->bus == b->bus &&
-        (a->slot != b->slot ||
-         a->function != b->function);
-}
+    pciDeviceList *activeDevs = data;
 
-static int
-pciBusContainsOtherDevices(virConnectPtr conn, pciDevice *dev)
-{
-    pciDevice *matched = NULL;
-    if (pciIterDevices(conn, pciSharesBus, dev, &matched) < 0)
-        return 1;
-    if (!matched)
+    if (dev->domain != check->domain ||
+        dev->bus != check->bus ||
+        (check->slot == check->slot &&
+         check->function == check->function))
+        return 0;
+
+    if (activeDevs && !pciDeviceListFind(activeDevs, check))
         return 0;
-    pciFreeDevice(conn, matched);
+
     return 1;
 }
 
-/* Is @a the parent of @b ? */
+static pciDevice *
+pciBusContainsActiveDevices(virConnectPtr conn,
+                            pciDevice *dev,
+                            pciDeviceList *activeDevs)
+{
+    pciDevice *active = NULL;
+    if (pciIterDevices(conn, pciSharesBusWithActive,
+                       dev, &active, activeDevs) < 0)
+        return NULL;
+    return active;
+}
+
+/* Is @check the parent of @dev ? */
 static int
-pciIsParent(pciDevice *a, pciDevice *b)
+pciIsParent(pciDevice *dev, pciDevice *check, void *data ATTRIBUTE_UNUSED)
 {
     uint16_t device_class;
     uint8_t header_type, secondary, subordinate;
 
-    if (a->domain != b->domain)
+    if (dev->domain != check->domain)
         return 0;
 
     /* Is it a bridge? */
-    device_class = pciRead16(a, PCI_CLASS_DEVICE);
+    device_class = pciRead16(check, PCI_CLASS_DEVICE);
     if (device_class != PCI_CLASS_BRIDGE_PCI)
         return 0;
 
     /* Is it a plane? */
-    header_type = pciRead8(a, PCI_HEADER_TYPE);
+    header_type = pciRead8(check, PCI_HEADER_TYPE);
     if ((header_type & PCI_HEADER_TYPE_MASK) != PCI_HEADER_TYPE_BRIDGE)
         return 0;
 
-    secondary   = pciRead8(a, PCI_SECONDARY_BUS);
-    subordinate = pciRead8(a, PCI_SUBORDINATE_BUS);
+    secondary   = pciRead8(check, PCI_SECONDARY_BUS);
+    subordinate = pciRead8(check, PCI_SUBORDINATE_BUS);
 
-    VIR_DEBUG("%s %s: found parent device %s\n", b->id, b->name, a->name);
+    VIR_DEBUG("%s %s: found parent device %s\n", dev->id, dev->name, check->name);
 
     /* No, it's superman! */
-    return (b->bus >= secondary && b->bus <= subordinate);
+    return (dev->bus >= secondary && dev->bus <= subordinate);
 }
 
 static pciDevice *
 pciGetParentDevice(virConnectPtr conn, pciDevice *dev)
 {
     pciDevice *parent = NULL;
-    pciIterDevices(conn, pciIsParent, dev, &parent);
+    pciIterDevices(conn, pciIsParent, dev, &parent, NULL);
     return parent;
 }
 
@@ -443,9 +452,11 @@ pciGetParentDevice(virConnectPtr conn, pciDevice *dev)
  * devices behind a bus.
  */
 static int
-pciTrySecondaryBusReset(virConnectPtr conn, pciDevice *dev)
+pciTrySecondaryBusReset(virConnectPtr conn,
+                        pciDevice *dev,
+                        pciDeviceList *activeDevs)
 {
-    pciDevice *parent;
+    pciDevice *parent, *conflict;
     uint8_t config_space[PCI_CONF_LEN];
     uint16_t ctl;
     int ret = -1;
@@ -455,10 +466,10 @@ pciTrySecondaryBusReset(virConnectPtr conn, pciDevice *dev)
      * In future, we could allow it so long as those devices
      * are not in use by the host or other guests.
      */
-    if (pciBusContainsOtherDevices(conn, dev)) {
+    if ((conflict = pciBusContainsActiveDevices(conn, dev, activeDevs))) {
         pciReportError(conn, VIR_ERR_NO_SUPPORT,
-                       _("Other devices on bus with %s, not doing bus reset"),
-                       dev->name);
+                       _("Active %s devices on bus with %s, not doing bus reset"),
+                       conflict->name, dev->name);
         return -1;
     }
 
@@ -572,10 +583,18 @@ pciInitDevice(virConnectPtr conn, pciDevice *dev)
 }
 
 int
-pciResetDevice(virConnectPtr conn, pciDevice *dev)
+pciResetDevice(virConnectPtr conn,
+               pciDevice *dev,
+               pciDeviceList *activeDevs)
 {
     int ret = -1;
 
+    if (activeDevs && pciDeviceListFind(activeDevs, dev)) {
+        pciReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                       _("Not resetting active device %s"), dev->name);
+        return -1;
+    }
+
     if (!dev->initted && pciInitDevice(conn, dev) < 0)
         return -1;
 
@@ -594,7 +613,7 @@ pciResetDevice(virConnectPtr conn, pciDevice *dev)
 
     /* Bus reset is not an option with the root bus */
     if (ret < 0 && dev->bus != 0)
-        ret = pciTrySecondaryBusReset(conn, dev);
+        ret = pciTrySecondaryBusReset(conn, dev, activeDevs);
 
     if (ret < 0) {
         virErrorPtr err = virGetLastError();
@@ -890,8 +909,116 @@ pciGetDevice(virConnectPtr conn,
 void
 pciFreeDevice(virConnectPtr conn ATTRIBUTE_UNUSED, pciDevice *dev)
 {
+    if (!dev)
+        return;
     VIR_DEBUG("%s %s: freeing", dev->id, dev->name);
     if (dev->fd >= 0)
         close(dev->fd);
     VIR_FREE(dev);
 }
+
+void pciDeviceSetManaged(pciDevice *dev, unsigned managed)
+{
+    dev->managed = !!managed;
+}
+
+unsigned pciDeviceGetManaged(pciDevice *dev)
+{
+    return dev->managed;
+}
+
+pciDeviceList *
+pciDeviceListNew(virConnectPtr conn)
+{
+    pciDeviceList *list;
+
+    if (VIR_ALLOC(list) < 0) {
+        virReportOOMError(conn);
+        return NULL;
+    }
+
+    return list;
+}
+
+void
+pciDeviceListFree(virConnectPtr conn,
+                  pciDeviceList *list)
+{
+    int i;
+
+    if (!list)
+        return;
+
+    for (i = 0; i < list->count; i++) {
+        pciFreeDevice(conn, list->devs[i]);
+        list->devs[i] = NULL;
+    }
+
+    list->count = 0;
+    VIR_FREE(list->devs);
+    VIR_FREE(list);
+}
+
+int
+pciDeviceListAdd(virConnectPtr conn,
+                 pciDeviceList *list,
+                 pciDevice *dev)
+{
+    if (pciDeviceListFind(list, dev)) {
+        pciReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                       _("Device %s is already in use"), dev->name);
+        return -1;
+    }
+
+    if (VIR_REALLOC_N(list->devs, list->count+1) < 0) {
+        virReportOOMError(conn);
+        return -1;
+    }
+
+    list->devs[list->count++] = dev;
+
+    return 0;
+}
+
+void
+pciDeviceListDel(virConnectPtr conn ATTRIBUTE_UNUSED,
+                 pciDeviceList *list,
+                 pciDevice *dev)
+{
+    int i;
+
+    for (i = 0; i < list->count; i++) {
+        if (list->devs[i]->domain   != dev->domain ||
+            list->devs[i]->bus      != dev->bus    ||
+            list->devs[i]->slot     != dev->slot   ||
+            list->devs[i]->function != dev->function)
+            continue;
+
+        pciFreeDevice(conn, list->devs[i]);
+
+        if (i != --list->count)
+            memmove(&list->devs[i],
+                    &list->devs[i+1],
+                    sizeof(*list->devs) * (list->count-i));
+
+        if (VIR_REALLOC_N(list->devs, list->count) < 0) {
+            ; /* not fatal */
+        }
+
+        break;
+    }
+}
+
+pciDevice *
+pciDeviceListFind(pciDeviceList *list, pciDevice *dev)
+{
+    int i;
+
+    for (i = 0; i < list->count; i++)
+        if (list->devs[i]->domain   == dev->domain &&
+            list->devs[i]->bus      == dev->bus    &&
+            list->devs[i]->slot     == dev->slot   &&
+            list->devs[i]->function == dev->function)
+            return list->devs[i];
+    return NULL;
+}
diff --git a/src/pci.h b/src/pci.h
index 47882ef..685b0af 100644
--- a/src/pci.h
+++ b/src/pci.h
@@ -27,6 +27,11 @@
 
 typedef struct _pciDevice pciDevice;
 
+typedef struct {
+    unsigned count;
+    pciDevice **devs;
+} pciDeviceList;
+
 pciDevice *pciGetDevice      (virConnectPtr  conn,
                               unsigned       domain,
                               unsigned       bus,
@@ -39,6 +44,22 @@ int        pciDettachDevice  (virConnectPtr  conn,
 int        pciReAttachDevice (virConnectPtr  conn,
                               pciDevice     *dev);
 int        pciResetDevice    (virConnectPtr  conn,
-                              pciDevice     *dev);
+                              pciDevice     *dev,
+                              pciDeviceList *activeDevs);
+void      pciDeviceSetManaged(pciDevice     *dev,
+                              unsigned       managed);
+unsigned  pciDeviceGetManaged(pciDevice     *dev);
+
+pciDeviceList *pciDeviceListNew  (virConnectPtr conn);
+void           pciDeviceListFree (virConnectPtr conn,
+                                  pciDeviceList *list);
+int            pciDeviceListAdd  (virConnectPtr conn,
+                                  pciDeviceList *list,
+                                  pciDevice *dev);
+void           pciDeviceListDel  (virConnectPtr conn,
+                                  pciDeviceList *list,
+                                  pciDevice *dev);
+pciDevice *    pciDeviceListFind (pciDeviceList *list,
+                                  pciDevice *dev);
 
 #endif /* __VIR_PCI_H__ */
diff --git a/src/qemu_conf.h b/src/qemu_conf.h
index 517626a..ab9d5e1 100644
--- a/src/qemu_conf.h
+++ b/src/qemu_conf.h
@@ -35,6 +35,7 @@
 #include "threads.h"
 #include "security.h"
 #include "cgroup.h"
+#include "pci.h"
 
 #define qemudDebug(fmt, ...) do {} while(0)
 
@@ -107,6 +108,8 @@ struct qemud_driver {
 
     char *securityDriverName;
     virSecurityDriverPtr securityDriver;
+
+    pciDeviceList *activePciHostdevs;
 };
 
 
diff --git a/src/qemu_driver.c b/src/qemu_driver.c
index fd39fc2..cbc27c4 100644
--- a/src/qemu_driver.c
+++ b/src/qemu_driver.c
@@ -128,6 +128,9 @@ static int qemudDomainSetMemoryBalloon(virConnectPtr conn,
 static int qemudDetectVcpuPIDs(virConnectPtr conn,
                                virDomainObjPtr vm);
 
+static int qemuUpdateActivePciHostdevs(struct qemud_driver *driver,
+                                       virDomainDefPtr def);
+
 static struct qemud_driver *qemu_driver = NULL;
 
 static int qemuCgroupControllerActive(struct qemud_driver *driver,
@@ -320,6 +323,10 @@ qemuReconnectDomain(struct qemud_driver *driver,
         goto error;
     }
 
+    if (qemuUpdateActivePciHostdevs(driver, obj->def) < 0) {
+        goto error;
+    }
+
     if (obj->def->seclabel.type == VIR_DOMAIN_SECLABEL_DYNAMIC &&
         driver->securityDriver &&
         driver->securityDriver->domainReserveSecurityLabel &&
@@ -524,6 +531,9 @@ qemudStartup(int privileged) {
     if ((qemu_driver->caps = qemudCapsInit(NULL)) == NULL)
         goto out_of_memory;
 
+    if ((qemu_driver->activePciHostdevs = pciDeviceListNew(NULL)) == NULL)
+        goto error;
+
     if (qemudLoadDriverConfig(qemu_driver, driverConf) < 0) {
         goto error;
     }
@@ -648,6 +658,7 @@ qemudShutdown(void) {
         return -1;
 
     qemuDriverLock(qemu_driver);
+    pciDeviceListFree(NULL, qemu_driver->activePciHostdevs);
     virCapabilitiesFree(qemu_driver->caps);
 
     virDomainObjListFree(&qemu_driver->domains);
@@ -1329,48 +1340,16 @@ static int qemudNextFreeVNCPort(struct qemud_driver *driver ATTRIBUTE_UNUSED) {
     return -1;
 }
 
-static int qemuPrepareHostDevices(virConnectPtr conn,
-                                  virDomainDefPtr def) {
+static pciDeviceList *
+qemuGetPciHostDeviceList(virConnectPtr conn,
+                         virDomainDefPtr def)
+{
+    pciDeviceList *list;
     int i;
 
-    /* We have to use 2 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
-     */
-
-    for (i = 0 ; i < def->nhostdevs ; i++) {
-        virDomainHostdevDefPtr hostdev = def->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->managed) {
-            pciDevice *dev = pciGetDevice(conn,
-                                          hostdev->source.subsys.u.pci.domain,
-                                          hostdev->source.subsys.u.pci.bus,
-                                          hostdev->source.subsys.u.pci.slot,
-                                          hostdev->source.subsys.u.pci.function);
-            if (!dev)
-                goto error;
-
-            if (pciDettachDevice(conn, dev) < 0) {
-                pciFreeDevice(conn, dev);
-                goto error;
-            }
-
-            pciFreeDevice(conn, dev);
-        } /* else {
-             XXX validate that non-managed device isn't in use, eg
-             by checking that device is either un-bound, or bound
-             to pci-stub.ko
-        } */
-    }
+    if (!(list = pciDeviceListNew(conn)))
+        return NULL;
 
-    /* Now that all the PCI hostdevs have be dettached, we can safely
-     * reset them */
     for (i = 0 ; i < def->nhostdevs ; i++) {
         virDomainHostdevDefPtr hostdev = def->hostdevs[i];
         pciDevice *dev;
@@ -1385,95 +1364,151 @@ static int qemuPrepareHostDevices(virConnectPtr conn,
                            hostdev->source.subsys.u.pci.bus,
                            hostdev->source.subsys.u.pci.slot,
                            hostdev->source.subsys.u.pci.function);
-        if (!dev)
-            goto error;
+        if (!dev) {
+            pciDeviceListFree(conn, list);
+            return NULL;
+        }
 
-        if (pciResetDevice(conn, dev) < 0) {
+        if (pciDeviceListAdd(conn, list, dev) < 0) {
             pciFreeDevice(conn, dev);
-            goto error;
+            pciDeviceListFree(conn, list);
+            return NULL;
         }
 
-        pciFreeDevice(conn, dev);
+        pciDeviceSetManaged(dev, hostdev->managed);
     }
 
-    return 0;
+    return list;
+}
 
-error:
-    return -1;
+static int
+qemuUpdateActivePciHostdevs(struct qemud_driver *driver,
+                            virDomainDefPtr def)
+{
+    pciDeviceList *pcidevs;
+    int i, ret;
+
+    if (!def->nhostdevs)
+        return 0;
+
+    if (!(pcidevs = qemuGetPciHostDeviceList(NULL, def)))
+        return -1;
+
+    ret = 0;
+
+    for (i = 0; i < pcidevs->count; i++) {
+        if (pciDeviceListAdd(NULL,
+                             driver->activePciHostdevs,
+                             pcidevs->devs[i]) < 0) {
+            ret = -1;
+            break;
+        }
+        pcidevs->devs[i] = NULL;
+    }
+
+    pciDeviceListFree(NULL, pcidevs);
+    return ret;
 }
 
-static void
-qemuDomainReAttachHostDevices(virConnectPtr conn, virDomainDefPtr def)
+static int
+qemuPrepareHostDevices(virConnectPtr conn,
+                       struct qemud_driver *driver,
+                       virDomainDefPtr def)
 {
+    pciDeviceList *pcidevs;
     int i;
 
-    /* Again 2 loops; reset all the devices before re-attach */
+    if (!def->nhostdevs)
+        return 0;
 
-    for (i = 0 ; i < def->nhostdevs ; i++) {
-        virDomainHostdevDefPtr hostdev = def->hostdevs[i];
-        pciDevice *dev;
+    if (!(pcidevs = qemuGetPciHostDeviceList(conn, def)))
+        return -1;
 
-        if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
-            continue;
-        if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
-            continue;
+    /* We have to use 3 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.
+     */
 
-        dev = pciGetDevice(conn,
-                           hostdev->source.subsys.u.pci.domain,
-                           hostdev->source.subsys.u.pci.bus,
-                           hostdev->source.subsys.u.pci.slot,
-                           hostdev->source.subsys.u.pci.function);
-        if (!dev) {
-            virErrorPtr err = virGetLastError();
-            VIR_ERROR(_("Failed to allocate pciDevice: %s\n"),
-                      err ? err->message : "");
-            virResetError(err);
-            continue;
-        }
+    /* XXX validate that non-managed device isn't in use, eg
+     * by checking that device is either un-bound, or bound
+     * to pci-stub.ko
+     */
 
-        if (pciResetDevice(conn, dev) < 0) {
-            virErrorPtr err = virGetLastError();
-            VIR_ERROR(_("Failed to reset PCI device: %s\n"),
-                      err ? err->message : "");
-            virResetError(err);
-        }
+    for (i = 0; i < pcidevs->count; i++)
+        if (pciDeviceGetManaged(pcidevs->devs[i]) &&
+            pciDettachDevice(conn, pcidevs->devs[i]) < 0)
+            goto error;
+
+    /* Now that all the PCI hostdevs have be dettached, we can safely
+     * reset them */
+    for (i = 0; i < pcidevs->count; i++)
+        if (pciResetDevice(conn, pcidevs->devs[i],
+                           driver->activePciHostdevs) < 0)
+            goto error;
 
-        pciFreeDevice(conn, dev);
+    /* Now mark all the devices as active */
+    for (i = 0; i < pcidevs->count; i++) {
+        if (pciDeviceListAdd(conn,
+                             driver->activePciHostdevs,
+                             pcidevs->devs[i]) < 0)
+            goto error;
+        pcidevs->devs[i] = NULL;
     }
 
-    for (i = 0 ; i < def->nhostdevs ; i++) {
-        virDomainHostdevDefPtr hostdev = def->hostdevs[i];
-        pciDevice *dev;
+    pciDeviceListFree(conn, pcidevs);
+    return 0;
 
-        if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
-            continue;
-        if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
-            continue;
-        if (!hostdev->managed)
-            continue;
+error:
+    pciDeviceListFree(conn, pcidevs);
+    return -1;
+}
 
-        dev = pciGetDevice(conn,
-                           hostdev->source.subsys.u.pci.domain,
-                           hostdev->source.subsys.u.pci.bus,
-                           hostdev->source.subsys.u.pci.slot,
-                           hostdev->source.subsys.u.pci.function);
-        if (!dev) {
+static void
+qemuDomainReAttachHostDevices(virConnectPtr conn,
+                              struct qemud_driver *driver,
+                              virDomainDefPtr def)
+{
+    pciDeviceList *pcidevs;
+    int i;
+
+    if (!def->nhostdevs)
+        return;
+
+    if (!(pcidevs = qemuGetPciHostDeviceList(conn, def))) {
+        virErrorPtr err = virGetLastError();
+        VIR_ERROR(_("Failed to allocate pciDeviceList: %s\n"),
+                  err ? err->message : "");
+        virResetError(err);
+        return;
+    }
+
+    /* Again 3 loops; mark all devices as inactive before reset
+     * them and reset all the devices before re-attach */
+
+    for (i = 0; i < pcidevs->count; i++)
+        pciDeviceListDel(conn, driver->activePciHostdevs, pcidevs->devs[i]);
+
+    for (i = 0; i < pcidevs->count; i++)
+        if (pciResetDevice(conn, pcidevs->devs[i],
+                           driver->activePciHostdevs) < 0) {
             virErrorPtr err = virGetLastError();
-            VIR_ERROR(_("Failed to allocate pciDevice: %s\n"),
+            VIR_ERROR(_("Failed to reset PCI device: %s\n"),
                       err ? err->message : "");
             virResetError(err);
-            continue;
         }
 
-        if (pciReAttachDevice(conn, dev) < 0) {
+    for (i = 0; i < pcidevs->count; i++)
+        if (pciDeviceGetManaged(pcidevs->devs[i]) &&
+            pciReAttachDevice(conn, pcidevs->devs[i]) < 0) {
             virErrorPtr err = virGetLastError();
             VIR_ERROR(_("Failed to re-attach PCI device: %s\n"),
                       err ? err->message : "");
             virResetError(err);
         }
 
-        pciFreeDevice(conn, dev);
-    }
+    pciDeviceListFree(conn, pcidevs);
 }
 
 static const char *const defaultDeviceACL[] = {
@@ -2001,7 +2036,7 @@ static int qemudStartVMDaemon(virConnectPtr conn,
     if (qemuSetupCgroup(conn, driver, vm) < 0)
         goto cleanup;
 
-    if (qemuPrepareHostDevices(conn, vm->def) < 0)
+    if (qemuPrepareHostDevices(conn, driver, vm->def) < 0)
         goto cleanup;
 
     if (VIR_ALLOC(vm->monitor_chr) < 0) {
@@ -2183,7 +2218,7 @@ static void qemudShutdownVMDaemon(virConnectPtr conn,
         VIR_WARN("Failed to restore all device ownership for %s",
                  vm->def->name);
 
-    qemuDomainReAttachHostDevices(conn, vm->def);
+    qemuDomainReAttachHostDevices(conn, driver, vm->def);
 
 retry:
     if ((ret = qemuRemoveCgroup(conn, driver, vm)) < 0) {
@@ -6791,6 +6826,7 @@ out:
 static int
 qemudNodeDeviceReset (virNodeDevicePtr dev)
 {
+    struct qemud_driver *driver = dev->conn->privateData;
     pciDevice *pci;
     unsigned domain, bus, slot, function;
     int ret = -1;
@@ -6802,11 +6838,14 @@ qemudNodeDeviceReset (virNodeDevicePtr dev)
     if (!pci)
         return -1;
 
-    if (pciResetDevice(dev->conn, pci) < 0)
+    qemuDriverLock(driver);
+
+    if (pciResetDevice(dev->conn, pci, driver->activePciHostdevs) < 0)
         goto out;
 
     ret = 0;
 out:
+    qemuDriverUnlock(driver);
     pciFreeDevice(dev->conn, pci);
     return ret;
 }
diff --git a/src/xen_unified.c b/src/xen_unified.c
index f2ffc25..dfa9ca5 100644
--- a/src/xen_unified.c
+++ b/src/xen_unified.c
@@ -1641,7 +1641,7 @@ xenUnifiedNodeDeviceReset (virNodeDevicePtr dev)
     if (!pci)
         return -1;
 
-    if (pciResetDevice(dev->conn, pci) < 0)
+    if (pciResetDevice(dev->conn, pci, NULL) < 0)
         goto out;
 
     ret = 0;
-- 
1.6.2.5