958e1b
From fdad8c197b91f1010e4f61147f27513a4f061e40 Mon Sep 17 00:00:00 2001
958e1b
From: Alex Williamson <alex.williamson@redhat.com>
958e1b
Date: Mon, 12 Jan 2015 04:52:17 +0100
958e1b
Subject: [PATCH 1/3] vfio-pci: Fix interrupt disabling
958e1b
958e1b
Message-id: <20150112045144.9215.59820.stgit@gimli.home>
958e1b
Patchwork-id: 63242
958e1b
O-Subject: [RHEL7.1 qemu-kvm PATCH] vfio-pci: Fix interrupt disabling
958e1b
Bugzilla: 1180942
958e1b
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
958e1b
RH-Acked-by: Bandan Das <bsd@redhat.com>
958e1b
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
958e1b
958e1b
Upstream: b3e27c3aee8f5a96debfe0346e9c0e3a641a8516
958e1b
958e1b
When disabling MSI/X interrupts the disable functions will leave the
958e1b
device in INTx mode (when available).  This matches how hardware
958e1b
operates, INTx is enabled unless MSI/X is enabled (DisINTx is handled
958e1b
separately).  Therefore when we really want to disable all interrupts,
958e1b
such as when removing the device, and we start with the device in
958e1b
MSI/X mode, we need to pass through INTx on our way to being
958e1b
completely quiesced.
958e1b
958e1b
In well behaved situations, the guest driver will have shutdown the
958e1b
device and it will start vfio_exitfn() in INTx mode, producing the
958e1b
desired result.  If hot-unplug causes the guest to crash, we may get
958e1b
the device in MSI/X state, which will leave QEMU with a bogus handler
958e1b
installed.
958e1b
958e1b
Fix this by re-ordering our disable routine so that it should always
958e1b
finish in VFIO_INT_NONE state, which is what all callers expect.
958e1b
958e1b
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
958e1b
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
958e1b
---
958e1b
 hw/misc/vfio.c | 21 ++++++++++++---------
958e1b
 1 file changed, 12 insertions(+), 9 deletions(-)
958e1b
958e1b
diff --git a/hw/misc/vfio.c b/hw/misc/vfio.c
958e1b
index abaa4c1..40b0923 100644
958e1b
--- a/hw/misc/vfio.c
958e1b
+++ b/hw/misc/vfio.c
958e1b
@@ -2353,16 +2353,19 @@ static void vfio_listener_release(VFIOContainer *container)
958e1b
  */
958e1b
 static void vfio_disable_interrupts(VFIODevice *vdev)
958e1b
 {
958e1b
-    switch (vdev->interrupt) {
958e1b
-    case VFIO_INT_INTx:
958e1b
-        vfio_disable_intx(vdev);
958e1b
-        break;
958e1b
-    case VFIO_INT_MSI:
958e1b
-        vfio_disable_msi(vdev);
958e1b
-        break;
958e1b
-    case VFIO_INT_MSIX:
958e1b
+    /*
958e1b
+     * More complicated than it looks.  Disabling MSI/X transitions the
958e1b
+     * device to INTx mode (if supported).  Therefore we need to first
958e1b
+     * disable MSI/X and then cleanup by disabling INTx.
958e1b
+     */
958e1b
+    if (vdev->interrupt == VFIO_INT_MSIX) {
958e1b
         vfio_disable_msix(vdev);
958e1b
-        break;
958e1b
+    } else if (vdev->interrupt == VFIO_INT_MSI) {
958e1b
+        vfio_disable_msi(vdev);
958e1b
+    }
958e1b
+
958e1b
+    if (vdev->interrupt == VFIO_INT_INTx) {
958e1b
+        vfio_disable_intx(vdev);
958e1b
     }
958e1b
 }
958e1b
 
958e1b
-- 
958e1b
1.8.3.1
958e1b