Blob Blame History Raw
From 1e44604c0d4c2d4c1347c2a1027f1ea02a6499f9 Mon Sep 17 00:00:00 2001
From: Mark McLoughlin <markmc@redhat.com>
Date: Fri, 14 Aug 2009 08:31:11 +0100
Subject: [PATCH] Reset and re-attach PCI host devices on guest shutdown

https://bugzilla.redhat.com/499561

When the guest shuts down, we should attempt to restore all PCI host
devices to a sane state.

In the case of managed hostdevs, we should reset and re-attach the
devices. In the case of unmanaged hostdevs, we should just reset them.

Note, KVM will already reset assigned devices when the guest shuts
down using whatever means it can, so we are only doing it to cover the
cases the kernel can't handle.

* src/qemu_driver.c: add qemuDomainReAttachHostDevices() and call
  it from qemudShutdownVMDaemon()

(cherry picked from commit 4035152a8767e72fd4e26a91cb4d5afa75b72e61)

Fedora-patch: libvirt-reattach-pci-hostdevs-after-guest-shutdown.patch
---
 src/qemu_driver.c |   76 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 76 insertions(+), 0 deletions(-)

diff --git a/src/qemu_driver.c b/src/qemu_driver.c
index ce04beb..ddd3693 100644
--- a/src/qemu_driver.c
+++ b/src/qemu_driver.c
@@ -1282,6 +1282,80 @@ error:
     return -1;
 }
 
+static void
+qemuDomainReAttachHostDevices(virConnectPtr conn, virDomainDefPtr def)
+{
+    int i;
+
+    /* Again 2 loops; reset all the devices before re-attach */
+
+    for (i = 0 ; i < def->nhostdevs ; i++) {
+        virDomainHostdevDefPtr hostdev = def->hostdevs[i];
+        pciDevice *dev;
+
+        if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
+            continue;
+        if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
+            continue;
+
+        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;
+        }
+
+        if (pciResetDevice(conn, dev) < 0) {
+            virErrorPtr err = virGetLastError();
+            VIR_ERROR(_("Failed to reset PCI device: %s\n"),
+                      err ? err->message : "");
+            virResetError(err);
+        }
+
+        pciFreeDevice(conn, dev);
+    }
+
+    for (i = 0 ; i < def->nhostdevs ; i++) {
+        virDomainHostdevDefPtr hostdev = def->hostdevs[i];
+        pciDevice *dev;
+
+        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;
+
+        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;
+        }
+
+        if (pciDettachDevice(conn, dev) < 0) {
+            virErrorPtr err = virGetLastError();
+            VIR_ERROR(_("Failed to reset PCI device: %s\n"),
+                      err ? err->message : "");
+            virResetError(err);
+        }
+
+        pciFreeDevice(conn, dev);
+    }
+}
+
 static int qemudDomainSetSecurityLabel(virConnectPtr conn, struct qemud_driver *driver, virDomainObjPtr vm)
 {
     if (vm->def->seclabel.label != NULL)
@@ -1560,6 +1634,8 @@ static void qemudShutdownVMDaemon(virConnectPtr conn ATTRIBUTE_UNUSED,
         VIR_FREE(vm->def->seclabel.imagelabel);
     }
 
+    qemuDomainReAttachHostDevices(conn, vm->def);
+
     if (qemudRemoveDomainStatus(conn, driver, vm) < 0) {
         VIR_WARN(_("Failed to remove domain status for %s"),
                  vm->def->name);
-- 
1.6.2.5