commit 6be610bfaae08655eaf93f9638d4c6636c00343f
Author: Osier Yang <jyang@redhat.com>
Date:   Wed Jan 18 04:02:05 2012 +0800

    qemu: Introduce inactive PCI device list
    
    pciTrySecondaryBusReset checks if there is active device on the
    same bus, however, qemu driver doesn't maintain an effective
    list for the inactive devices, and it passes meaningless argument
    for parameter "inactiveDevs". e.g. (qemuPrepareHostdevPCIDevices)
    
    if (!(pcidevs = qemuGetPciHostDeviceList(hostdevs, nhostdevs)))
        return -1;
    
    ..skipped...
    
    if (pciResetDevice(dev, driver->activePciHostdevs, pcidevs) < 0)
        goto reattachdevs;
    
    NB, the "pcidevs" used above are extracted from domain def, and
    thus one won't be able to attach a device of which bus has other
    device even detached from host (nodedev-detach). To see more
    details of the problem:
    
    RHBZ: https://bugzilla.redhat.com/show_bug.cgi?id=773667
    
    This patch is to resolve the problem by introducing an inactive
    PCI device list (just like qemu_driver->activePciHostdevs), and
    the whole logic is:
    
      * Add the device to inactive list during nodedev-dettach
      * Remove the device from inactive list during nodedev-reattach
      * Remove the device from inactive list during attach-device
        (for non-managed device)
      * Add the device to inactive list after detach-device, only
        if the device is not managed
    
    With the above, we have a sufficient inactive PCI device list, and thus
    we can use it for pciResetDevice. e.g.(qemuPrepareHostdevPCIDevices)
    
    if (pciResetDevice(dev, driver->activePciHostdevs,
                       driver->inactivePciHostdevs) < 0)
        goto reattachdevs;

Index: libvirt-0.9.6/src/qemu/qemu_conf.h
===================================================================
--- libvirt-0.9.6.orig/src/qemu/qemu_conf.h
+++ libvirt-0.9.6/src/qemu/qemu_conf.h
@@ -1,7 +1,7 @@
 /*
  * qemu_conf.h: QEMU configuration management
  *
- * Copyright (C) 2006-2007, 2009-2011 Red Hat, Inc.
+ * Copyright (C) 2006-2007, 2009-2012 Red Hat, Inc.
  * Copyright (C) 2006 Daniel P. Berrange
  *
  * This library is free software; you can redistribute it and/or
@@ -128,6 +128,9 @@ struct qemud_driver {
 
     pciDeviceList *activePciHostdevs;
 
+    /* The devices which is are not in use by the host or any guest. */
+    pciDeviceList *inactivePciHostdevs;
+
     virBitmapPtr reservedVNCPorts;
 
     virSysinfoDefPtr hostsysinfo;
Index: libvirt-0.9.6/src/qemu/qemu_driver.c
===================================================================
--- libvirt-0.9.6.orig/src/qemu/qemu_driver.c
+++ libvirt-0.9.6/src/qemu/qemu_driver.c
@@ -577,6 +577,9 @@ qemudStartup(int privileged) {
     if ((qemu_driver->activePciHostdevs = pciDeviceListNew()) == NULL)
         goto error;
 
+    if ((qemu_driver->inactivePciHostdevs = pciDeviceListNew()) == NULL)
+        goto error;
+
     if (privileged) {
         if (chown(qemu_driver->libDir, qemu_driver->user, qemu_driver->group) < 0) {
             virReportSystemError(errno,
@@ -767,6 +770,7 @@ qemudShutdown(void) {
 
     qemuDriverLock(qemu_driver);
     pciDeviceListFree(qemu_driver->activePciHostdevs);
+    pciDeviceListFree(qemu_driver->inactivePciHostdevs);
     virCapabilitiesFree(qemu_driver->caps);
 
     virDomainObjListDeinit(&qemu_driver->domains);
@@ -8296,6 +8300,7 @@ qemudNodeDeviceDettach (virNodeDevicePtr
     pciDevice *pci;
     unsigned domain, bus, slot, function;
     int ret = -1;
+    bool in_inactive_list = false;
 
     if (qemudNodeDeviceGetPciInfo(dev, &domain, &bus, &slot, &function) < 0)
         return -1;
@@ -8305,13 +8310,17 @@ qemudNodeDeviceDettach (virNodeDevicePtr
         return -1;
 
     qemuDriverLock(driver);
-    if (pciDettachDevice(pci, driver->activePciHostdevs) < 0)
+    in_inactive_list = pciDeviceListFind(driver->inactivePciHostdevs, pci);
+
+    if (pciDettachDevice(pci, driver->activePciHostdevs,
+                         driver->inactivePciHostdevs) < 0)
         goto out;
 
     ret = 0;
 out:
     qemuDriverUnlock(driver);
-    pciFreeDevice(pci);
+    if (in_inactive_list)
+        pciFreeDevice(pci);
     return ret;
 }
 
@@ -8333,7 +8342,8 @@ qemudNodeDeviceReAttach (virNodeDevicePt
     pciDeviceReAttachInit(pci);
 
     qemuDriverLock(driver);
-    if (pciReAttachDevice(pci, driver->activePciHostdevs) < 0)
+    if (pciReAttachDevice(pci, driver->activePciHostdevs,
+                          driver->inactivePciHostdevs) < 0)
         goto out;
 
     ret = 0;
@@ -8360,7 +8370,8 @@ qemudNodeDeviceReset (virNodeDevicePtr d
 
     qemuDriverLock(driver);
 
-    if (pciResetDevice(pci, driver->activePciHostdevs, NULL) < 0)
+    if (pciResetDevice(pci, driver->activePciHostdevs,
+                       driver->inactivePciHostdevs) < 0)
         goto out;
 
     ret = 0;
Index: libvirt-0.9.6/src/qemu/qemu_hostdev.c
===================================================================
--- libvirt-0.9.6.orig/src/qemu/qemu_hostdev.c
+++ libvirt-0.9.6/src/qemu/qemu_hostdev.c
@@ -1,7 +1,7 @@
 /*
  * qemu_hostdev.c: QEMU hostdev management
  *
- * Copyright (C) 2006-2007, 2009-2011 Red Hat, Inc.
+ * Copyright (C) 2006-2007, 2009-2012 Red Hat, Inc.
  * Copyright (C) 2006 Daniel P. Berrange
  *
  * This library is free software; you can redistribute it and/or
@@ -212,7 +212,7 @@ int qemuPrepareHostdevPCIDevices(struct
     for (i = 0; i < pciDeviceListCount(pcidevs); i++) {
         pciDevice *dev = pciDeviceListGet(pcidevs, i);
         if (pciDeviceGetManaged(dev) &&
-            pciDettachDevice(dev, driver->activePciHostdevs) < 0)
+            pciDettachDevice(dev, driver->activePciHostdevs, NULL) < 0)
             goto reattachdevs;
     }
 
@@ -220,7 +220,8 @@ int qemuPrepareHostdevPCIDevices(struct
      * can safely reset them */
     for (i = 0; i < pciDeviceListCount(pcidevs); i++) {
         pciDevice *dev = pciDeviceListGet(pcidevs, i);
-        if (pciResetDevice(dev, driver->activePciHostdevs, pcidevs) < 0)
+        if (pciResetDevice(dev, driver->activePciHostdevs,
+                           driver->inactivePciHostdevs) < 0)
             goto reattachdevs;
     }
 
@@ -233,7 +234,13 @@ int qemuPrepareHostdevPCIDevices(struct
         }
     }
 
-    /* Loop 5: Now set the used_by_domain of the device in
+    /* Loop 5: Now remove the devices from inactive list. */
+    for (i = 0; i < pciDeviceListCount(pcidevs); i++) {
+         pciDevice *dev = pciDeviceListGet(pcidevs, i);
+         pciDeviceListDel(driver->inactivePciHostdevs, dev);
+    }
+
+    /* Loop 6: Now set the used_by_domain of the device in
      * driver->activePciHostdevs as domain name.
      */
     for (i = 0; i < pciDeviceListCount(pcidevs); i++) {
@@ -245,7 +252,7 @@ int qemuPrepareHostdevPCIDevices(struct
         pciDeviceSetUsedBy(activeDev, name);
     }
 
-    /* Loop 6: Now set the original states for hostdev def */
+    /* Loop 7: Now set the original states for hostdev def */
     for (i = 0; i < nhostdevs; i++) {
         pciDevice *dev;
         pciDevice *pcidev;
@@ -277,7 +284,7 @@ int qemuPrepareHostdevPCIDevices(struct
         pciFreeDevice(dev);
     }
 
-    /* Loop 7: Now steal all the devices from pcidevs */
+    /* Loop 8: Now steal all the devices from pcidevs */
     while (pciDeviceListCount(pcidevs) > 0) {
         pciDevice *dev = pciDeviceListGet(pcidevs, 0);
         pciDeviceListSteal(pcidevs, dev);
@@ -298,7 +305,7 @@ inactivedevs:
 reattachdevs:
     for (i = 0; i < pciDeviceListCount(pcidevs); i++) {
         pciDevice *dev = pciDeviceListGet(pcidevs, i);
-        pciReAttachDevice(dev, driver->activePciHostdevs);
+        pciReAttachDevice(dev, driver->activePciHostdevs, NULL);
     }
 
 cleanup:
@@ -367,8 +374,13 @@ void qemuReattachPciDevice(pciDevice *de
 {
     int retries = 100;
 
-    if (!pciDeviceGetManaged(dev))
+    /* If the device is not managed and was attached to guest
+     * successfully, it must have been inactive.
+     */
+    if (!pciDeviceGetManaged(dev)) {
+        pciDeviceListAdd(driver->inactivePciHostdevs, dev);
         return;
+    }
 
     while (pciWaitForDeviceCleanup(dev, "kvm_assigned_device")
            && retries) {
@@ -376,7 +388,8 @@ void qemuReattachPciDevice(pciDevice *de
         retries--;
     }
 
-    if (pciReAttachDevice(dev, driver->activePciHostdevs) < 0) {
+    if (pciReAttachDevice(dev, driver->activePciHostdevs,
+                          driver->inactivePciHostdevs) < 0) {
         virErrorPtr err = virGetLastError();
         VIR_ERROR(_("Failed to re-attach PCI device: %s"),
                   err ? err->message : _("unknown error"));
@@ -423,7 +436,8 @@ void qemuDomainReAttachHostdevDevices(st
 
     for (i = 0; i < pciDeviceListCount(pcidevs); i++) {
         pciDevice *dev = pciDeviceListGet(pcidevs, i);
-        if (pciResetDevice(dev, driver->activePciHostdevs, pcidevs) < 0) {
+        if (pciResetDevice(dev, driver->activePciHostdevs,
+                           driver->inactivePciHostdevs) < 0) {
             virErrorPtr err = virGetLastError();
             VIR_ERROR(_("Failed to reset PCI device: %s"),
                       err ? err->message : _("unknown error"));
Index: libvirt-0.9.6/src/qemu/qemu_hotplug.c
===================================================================
--- libvirt-0.9.6.orig/src/qemu/qemu_hotplug.c
+++ libvirt-0.9.6/src/qemu/qemu_hotplug.c
@@ -1984,7 +1984,8 @@ int qemuDomainDetachHostPciDevice(struct
                        detach->source.subsys.u.pci.function);
     if (pci) {
         activePci = pciDeviceListSteal(driver->activePciHostdevs, pci);
-        if (pciResetDevice(activePci, driver->activePciHostdevs, NULL))
+        if (pciResetDevice(activePci, driver->activePciHostdevs,
+                           driver->inactivePciHostdevs) == 0)
             qemuReattachPciDevice(activePci, driver);
         else
             ret = -1;
Index: libvirt-0.9.6/src/util/pci.c
===================================================================
--- libvirt-0.9.6.orig/src/util/pci.c
+++ libvirt-0.9.6/src/util/pci.c
@@ -1117,7 +1117,9 @@ cleanup:
 }
 
 int
-pciDettachDevice(pciDevice *dev, pciDeviceList *activeDevs)
+pciDettachDevice(pciDevice *dev,
+                 pciDeviceList *activeDevs,
+                 pciDeviceList *inactiveDevs)
 {
     const char *driver = pciFindStubDriver();
     if (!driver) {
@@ -1132,11 +1134,22 @@ pciDettachDevice(pciDevice *dev, pciDevi
         return -1;
     }
 
-    return pciBindDeviceToStub(dev, driver);
+    if (pciBindDeviceToStub(dev, driver) < 0)
+        return -1;
+
+    /* Add the dev into list inactiveDevs */
+    if (inactiveDevs && !pciDeviceListFind(inactiveDevs, dev)) {
+        if (pciDeviceListAdd(inactiveDevs, dev) < 0)
+            return -1;
+    }
+
+    return 0;
 }
 
 int
-pciReAttachDevice(pciDevice *dev, pciDeviceList *activeDevs)
+pciReAttachDevice(pciDevice *dev,
+                  pciDeviceList *activeDevs,
+                  pciDeviceList *inactiveDevs)
 {
     const char *driver = pciFindStubDriver();
     if (!driver) {
@@ -1151,7 +1164,14 @@ pciReAttachDevice(pciDevice *dev, pciDev
         return -1;
     }
 
-    return pciUnbindDeviceFromStub(dev, driver);
+    if (pciUnbindDeviceFromStub(dev, driver) < 0)
+        return -1;
+
+    /* Steal the dev from list inactiveDevs */
+    if (inactiveDevs)
+        pciDeviceListSteal(inactiveDevs, dev);
+
+    return 0;
 }
 
 /* Certain hypervisors (like qemu/kvm) map the PCI bar(s) on
Index: libvirt-0.9.6/src/util/pci.h
===================================================================
--- libvirt-0.9.6.orig/src/util/pci.h
+++ libvirt-0.9.6/src/util/pci.h
@@ -40,8 +40,12 @@ pciDevice *pciGetDevice      (unsigned
                               unsigned       function);
 void       pciFreeDevice     (pciDevice     *dev);
 const char *pciDeviceGetName (pciDevice     *dev);
-int        pciDettachDevice  (pciDevice     *dev, pciDeviceList *activeDevs);
-int        pciReAttachDevice (pciDevice     *dev, pciDeviceList *activeDevs);
+int        pciDettachDevice  (pciDevice     *dev,
+                              pciDeviceList *activeDevs,
+                              pciDeviceList *inactiveDevs);
+int        pciReAttachDevice (pciDevice     *dev,
+                              pciDeviceList *activeDevs,
+                              pciDeviceList *inactiveDevs);
 int        pciResetDevice    (pciDevice     *dev,
                               pciDeviceList *activeDevs,
                               pciDeviceList *inactiveDevs);
Index: libvirt-0.9.6/src/xen/xen_driver.c
===================================================================
--- libvirt-0.9.6.orig/src/xen/xen_driver.c
+++ libvirt-0.9.6/src/xen/xen_driver.c
@@ -1994,7 +1994,7 @@ xenUnifiedNodeDeviceDettach (virNodeDevi
     if (!pci)
         return -1;
 
-    if (pciDettachDevice(pci, NULL) < 0)
+    if (pciDettachDevice(pci, NULL, NULL) < 0)
         goto out;
 
     ret = 0;
@@ -2084,7 +2084,7 @@ xenUnifiedNodeDeviceReAttach (virNodeDev
         goto out;
     }
 
-    if (pciReAttachDevice(pci, NULL) < 0)
+    if (pciReAttachDevice(pci, NULL, NULL) < 0)
         goto out;
 
     ret = 0;
