diff options
author | Alex Williamson <alex.williamson@redhat.com> | 2015-01-09 08:50:53 -0700 |
---|---|---|
committer | Alex Williamson <alex.williamson@redhat.com> | 2015-01-09 08:50:53 -0700 |
commit | b3e27c3aee8f5a96debfe0346e9c0e3a641a8516 (patch) | |
tree | 7c961b361bdf7224460ce32fdbc4344f6df835db /hw/vfio | |
parent | 29c6e6df492d81b1843e5dd999171bb84c6effea (diff) |
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/vfio')
-rw-r--r-- | hw/vfio/pci.c | 21 |
1 files changed, 12 insertions, 9 deletions
diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index b6703c7d37..014a92ce5f 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); } } |