commit 52b01f0f8fa095727d589e26b8b6f8a94ac23b08 Author: Alex Williamson Date: Wed Dec 19 15:41:38 2012 -0700 pci-assign: Enable MSIX on device to match guest When a Linux guest enables MSIX on a device we evaluate the MSIX vector table, find no unmasked vectors and don't switch the device to MSIX mode. Typically this works fine and the device will be switched lazily once the guest enables (unmasks) a vector. Unfortunately some drivers enable MSIX, then use interfaces to send commands between VF & PF or PF & firmware that act based on the physical state of the device. These may fail if the host side sees something different than the guest. Previously pci-assign looked at the data field of the vector table to try to guess which vectors would be enabled. That can end up wasting a lot of vectors on the host for some devices. We're a bit limited by the legacy PCI assignment interfaces but we can still take a hybrid approach and always enable a single vector if MSI-X is enabled with no unmasked vectors. We don't bother with the data field here because we know some guests (FreeBSD) don't program the vector table when MSI-X is enabled and we don't want MSI-X enabled lazily on some guests and synchronously on others. Ideally we could find a safe value to program for the MSIMessage, but for the predominant guests this ends up being the same we did for a long time, except we save some vectors by limiting it to one. Signed-off-by: Alex Williamson diff --git a/hw/kvm/pci-assign.c b/hw/kvm/pci-assign.c index e80dad0..acfd11c 100644 --- a/hw/kvm/pci-assign.c +++ b/hw/kvm/pci-assign.c @@ -1032,22 +1032,29 @@ static int assigned_dev_update_msix_mmio(PCIDevice *pci_dev) int i, r = 0; MSIXTableEntry *entry = adev->msix_table; MSIMessage msg; + bool no_vectors = true; /* Get the usable entry number for allocating */ for (i = 0; i < adev->msix_max; i++, entry++) { if (assigned_dev_msix_masked(entry)) { continue; } + no_vectors = false; entries_nr++; } - DEBUG("MSI-X entries: %d\n", entries_nr); - - /* It's valid to enable MSI-X with all entries masked */ - if (!entries_nr) { - return 0; + /* + * Always enable at least one vector. Not all drivers can tolerate + * lazily enabling MSI-X when the first vector is unmasked. VF/PF + * and PF/fw channels may rely on the state of the physical device. + * If no vectors are unmasked, just enable the first vector. + */ + if (no_vectors) { + entries_nr = 1; } + DEBUG("MSI-X entries: %d%s\n", entries_nr, no_vectors ? " forced" : ""); + r = kvm_device_msix_init_vectors(kvm_state, adev->dev_id, entries_nr); if (r != 0) { error_report("fail to set MSI-X entry number for MSIX! %s", @@ -1064,10 +1071,17 @@ static int assigned_dev_update_msix_mmio(PCIDevice *pci_dev) for (i = 0; i < adev->msix_max; i++, entry++) { adev->msi_virq[i] = -1; - if (assigned_dev_msix_masked(entry)) { + if (assigned_dev_msix_masked(entry) && !(i == 0 && no_vectors)) { continue; } + /* + * XXX In the no_vectors case, can we program something safe here + * rather than whatever contents might be in the vector table? + * For Linux & Windows guests there's a good chance of finding + * something valid here, but not everyone writes to the vector + * table before getting here (FreeBSD). + */ msg.address = entry->addr_lo | ((uint64_t)entry->addr_hi << 32); msg.data = entry->data; r = kvm_irqchip_add_msi_route(kvm_state, msg);