Check active domain hostdevs before allowing PCI reset
authorMark McLoughlin <markmc@redhat.com>
Fri, 14 Aug 2009 07:31:11 +0000 (08:31 +0100)
committerMark McLoughlin <markmc@redhat.com>
Fri, 14 Aug 2009 07:31:11 +0000 (08:31 +0100)
If a PCI device reset causes other devices to be reset, allow it so long
as those other devices are note assigned to another active domain.

Note, we need to take the driver lock qemudNodeDeviceReset() because the
check function will iterate over the domain list.

* src/qemu_conf.c: add qemuCheckPciHostDevice() to iterate over active
  domains checking whether the affected device is assigned

* src/pci.[ch]: add pciDeviceEquals() helper

src/libvirt_private.syms
src/pci.c
src/pci.h
src/qemu_driver.c

index 642c2bc..23fa01b 100644 (file)
@@ -283,6 +283,7 @@ virNodeDeviceAssignDef;
 pciGetDevice;
 pciFreeDevice;
 pciDettachDevice;
+pciDeviceEquals;
 pciReAttachDevice;
 pciResetDevice;
 
index 6a2e860..619853b 100644 (file)
--- a/src/pci.c
+++ b/src/pci.c
@@ -926,3 +926,18 @@ pciFreeDevice(virConnectPtr conn ATTRIBUTE_UNUSED, pciDevice *dev)
         close(dev->fd);
     VIR_FREE(dev);
 }
+
+int
+pciDeviceEquals(virConnectPtr  conn ATTRIBUTE_UNUSED,
+                pciDevice     *dev,
+                unsigned       domain,
+                unsigned       bus,
+                unsigned       slot,
+                unsigned       function)
+{
+    return
+        dev->domain   == domain &&
+        dev->bus      == bus &&
+        dev->slot     == slot &&
+        dev->function == function;
+}
index 15da057..d5e680c 100644 (file)
--- a/src/pci.h
+++ b/src/pci.h
@@ -52,4 +52,11 @@ int pciResetDevice(virConnectPtr      conn,
                    pciDevice         *dev,
                    pciResetCheckFunc  check);
 
+int pciDeviceEquals(virConnectPtr  conn,
+                    pciDevice     *dev,
+                    unsigned       domain,
+                    unsigned       bus,
+                    unsigned       slot,
+                    unsigned       function);
+
 #endif /* __VIR_PCI_H__ */
index bfa06a5..4b1aeea 100644 (file)
@@ -1330,6 +1330,48 @@ static int qemudNextFreeVNCPort(struct qemud_driver *driver ATTRIBUTE_UNUSED) {
 }
 
 static int
+qemuCheckPciHostDevice(virConnectPtr conn,
+                       virDomainObjPtr owner_vm,
+                       pciDevice *dev)
+{
+    struct qemud_driver *driver = conn->privateData;
+    int ret = 1, i;
+
+    for (i = 0; i < driver->domains.count && ret; i++) {
+        virDomainObjPtr vm = driver->domains.objs[i];
+
+        if (vm == owner_vm)
+            continue;
+
+        virDomainObjLock(vm);
+
+        if (virDomainIsActive(vm)) {
+            int j;
+
+            for (j = 0; j < vm->def->nhostdevs && ret; j++) {
+                virDomainHostdevDefPtr hostdev = vm->def->hostdevs[j];
+
+                if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
+                    continue;
+                if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
+                    continue;
+
+                if (pciDeviceEquals(conn, dev,
+                                    hostdev->source.subsys.u.pci.domain,
+                                    hostdev->source.subsys.u.pci.bus,
+                                    hostdev->source.subsys.u.pci.slot,
+                                    hostdev->source.subsys.u.pci.function))
+                    ret = 0;
+            }
+        }
+
+        virDomainObjUnlock(vm);
+    }
+
+    return ret;
+}
+
+static int
 qemuPrepareHostDevices(virConnectPtr conn, virDomainObjPtr vm)
 {
     virDomainDefPtr def = vm->def;
@@ -1390,7 +1432,7 @@ qemuPrepareHostDevices(virConnectPtr conn, virDomainObjPtr vm)
         if (!dev)
             goto error;
 
-        if (pciResetDevice(conn, vm, dev, NULL) < 0) {
+        if (pciResetDevice(conn, vm, dev, qemuCheckPciHostDevice) < 0) {
             pciFreeDevice(conn, dev);
             goto error;
         }
@@ -1434,7 +1476,7 @@ qemuDomainReAttachHostDevices(virConnectPtr conn, virDomainObjPtr vm)
             continue;
         }
 
-        if (pciResetDevice(conn, vm, dev, NULL) < 0) {
+        if (pciResetDevice(conn, vm, dev, qemuCheckPciHostDevice) < 0) {
             virErrorPtr err = virGetLastError();
             VIR_ERROR(_("Failed to reset PCI device: %s\n"),
                       err ? err->message : "");
@@ -5250,7 +5292,7 @@ static int qemudDomainAttachHostPciDevice(virConnectPtr conn,
             return -1;
 
         if (pciDettachDevice(conn, pci) < 0 ||
-            pciResetDevice(conn, vm, pci, NULL) < 0) {
+            pciResetDevice(conn, vm, pci, qemuCheckPciHostDevice) < 0) {
             pciFreeDevice(conn, pci);
             return -1;
         }
@@ -7041,6 +7083,7 @@ out:
 static int
 qemudNodeDeviceReset (virNodeDevicePtr dev)
 {
+    struct qemud_driver *driver = dev->conn->privateData;
     pciDevice *pci;
     unsigned domain, bus, slot, function;
     int ret = -1;
@@ -7052,11 +7095,14 @@ qemudNodeDeviceReset (virNodeDevicePtr dev)
     if (!pci)
         return -1;
 
-    if (pciResetDevice(dev->conn, NULL, pci, NULL) < 0)
+    qemuDriverLock(driver);
+
+    if (pciResetDevice(dev->conn, NULL, pci, qemuCheckPciHostDevice) < 0)
         goto out;
 
     ret = 0;
 out:
+    qemuDriverUnlock(driver);
     pciFreeDevice(dev->conn, pci);
     return ret;
 }