aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorAlex Williamson <alex.williamson@redhat.com>2015-01-09 08:50:53 -0700
committerAlex Williamson <alex.williamson@redhat.com>2015-01-09 08:50:53 -0700
commitb3e27c3aee8f5a96debfe0346e9c0e3a641a8516 (patch)
tree7c961b361bdf7224460ce32fdbc4344f6df835db /hw
parent29c6e6df492d81b1843e5dd999171bb84c6effea (diff)
downloadqemu-b3e27c3aee8f5a96debfe0346e9c0e3a641a8516.zip
qemu-b3e27c3aee8f5a96debfe0346e9c0e3a641a8516.tar.gz
qemu-b3e27c3aee8f5a96debfe0346e9c0e3a641a8516.tar.bz2
vfio-pci: Fix interrupt disabling
When disabling MSI/X interrupts the disable functions will leave the device in INTx mode (when available). This matches how hardware operates, INTx is enabled unless MSI/X is enabled (DisINTx is handled separately). Therefore when we really want to disable all interrupts, such as when removing the device, and we start with the device in MSI/X mode, we need to pass through INTx on our way to being completely quiesced. In well behaved situations, the guest driver will have shutdown the device and it will start vfio_exitfn() in INTx mode, producing the desired result. If hot-unplug causes the guest to crash, we may get the device in MSI/X state, which will leave QEMU with a bogus handler installed. Fix this by re-ordering our disable routine so that it should always finish in VFIO_INT_NONE state, which is what all callers expect. Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
Diffstat (limited to 'hw')
-rw-r--r--hw/vfio/pci.c21
1 files changed, 12 insertions, 9 deletions
diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index b6703c7..014a92c 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -2129,16 +2129,19 @@ static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr,
*/
static void vfio_disable_interrupts(VFIOPCIDevice *vdev)
{
- switch (vdev->interrupt) {
- case VFIO_INT_INTx:
- vfio_disable_intx(vdev);
- break;
- case VFIO_INT_MSI:
- vfio_disable_msi(vdev);
- break;
- case VFIO_INT_MSIX:
+ /*
+ * More complicated than it looks. Disabling MSI/X transitions the
+ * device to INTx mode (if supported). Therefore we need to first
+ * disable MSI/X and then cleanup by disabling INTx.
+ */
+ if (vdev->interrupt == VFIO_INT_MSIX) {
vfio_disable_msix(vdev);
- break;
+ } else if (vdev->interrupt == VFIO_INT_MSI) {
+ vfio_disable_msi(vdev);
+ }
+
+ if (vdev->interrupt == VFIO_INT_INTx) {
+ vfio_disable_intx(vdev);
}
}