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>
This commit is contained in:
Alex Williamson 2015-01-09 08:50:53 -07:00
parent 29c6e6df49
commit b3e27c3aee
1 changed files with 12 additions and 9 deletions

View File

@ -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);
}
}