Implement MSI support of a assigned devices via the generic MSI layer of QEMU. Use config notifiers to update the vector route or switch back to INTx when MSI gets disabled again.
Using the generic layer not only saves a bit code, it also fixes reset while legacy MSI is in use and adds 64 bit support. Signed-off-by: Jan Kiszka <jan.kis...@siemens.com> --- hw/device-assignment.c | 77 +++++++++++++++++++---------------------------- 1 files changed, 31 insertions(+), 46 deletions(-) diff --git a/hw/device-assignment.c b/hw/device-assignment.c index 2484afd..10b30a3 100644 --- a/hw/device-assignment.c +++ b/hw/device-assignment.c @@ -699,10 +699,6 @@ static void free_assigned_device(AssignedDevice *dev) close(dev->real_device.config_fd); } - if (dev->dev.msi_cache) { - kvm_msi_cache_invalidate(&dev->dev.msi_cache[0]); - g_free(dev->dev.msi_cache); - } invalidate_msix_vectors(dev); g_free(dev->dev.msix_cache); } @@ -847,7 +843,7 @@ static int assign_intx(AssignedDevice *dev) irq_type = KVM_DEV_IRQ_GUEST_INTX; if (dev->features & ASSIGNED_DEVICE_PREFER_MSI_MASK && - dev->cap.available & ASSIGNED_DEVICE_CAP_MSI) { + msi_present(&dev->dev)) { irq_type |= KVM_DEV_IRQ_HOST_MSI; } else { irq_type |= KVM_DEV_IRQ_HOST_INTX; @@ -920,31 +916,33 @@ void assigned_dev_update_irqs(void) } } -static void assigned_dev_update_msi(PCIDevice *pci_dev) +static void assigned_dev_update_msi(PCIDevice *pci_dev, bool enabled) { AssignedDevice *dev = DO_UPCAST(AssignedDevice, dev, pci_dev); - uint8_t ctrl_byte = pci_get_byte(pci_dev->config + pci_dev->msi_cap + - PCI_MSI_FLAGS); - - if (ctrl_byte & PCI_MSI_FLAGS_ENABLE) { - uint8_t *pos = pci_dev->config + pci_dev->msi_cap; - MSIMessage msg; - deassign_irq(dev); + if (!enabled) { + assign_intx(dev); + } +} - msg.address = pci_get_long(pos + PCI_MSI_ADDRESS_LO); - msg.data = pci_get_word(pos + PCI_MSI_DATA_32); +static int assigned_dev_update_msi_vector(PCIDevice *pci_dev, + unsigned int vector, + MSIMessage *msg, bool masked) +{ + AssignedDevice *dev = DO_UPCAST(AssignedDevice, dev, pci_dev); + int ret; - if (kvm_device_msi_assign(kvm_state, calc_assigned_dev_id(dev), &msg, - &dev->dev.msi_cache[0]) < 0) { - perror("assigned_dev_update_msi: assign msi"); - return; + if (!masked) { + deassign_irq(dev); + ret = kvm_device_msi_assign(kvm_state, calc_assigned_dev_id(dev), msg, + &dev->dev.msi_cache[0]); + if (ret < 0) { + perror("assigned_dev_update_msi_vector: assign msi"); + return ret; } dev->irq_requested_type = KVM_DEV_IRQ_HOST_MSI | KVM_DEV_IRQ_GUEST_MSI; - } else { - kvm_msi_cache_invalidate(&dev->dev.msi_cache[0]); - assign_intx(dev); } + return 0; } static int assigned_dev_set_msix_vectors(PCIDevice *pci_dev) @@ -1085,12 +1083,6 @@ static void assigned_dev_pci_write_config(PCIDevice *pci_dev, uint32_t address, pci_default_write_config(pci_dev, address, val, len); - if (assigned_dev->cap.available & ASSIGNED_DEVICE_CAP_MSI) { - if (range_covers_byte(address, len, - pci_dev->msi_cap + PCI_MSI_FLAGS)) { - assigned_dev_update_msi(pci_dev); - } - } if (assigned_dev->cap.available & ASSIGNED_DEVICE_CAP_MSIX) { if (range_covers_byte(address, len, pci_dev->msix_cap + PCI_MSIX_FLAGS + 1)) { @@ -1136,26 +1128,19 @@ static int assigned_device_pci_cap_init(PCIDevice *pci_dev) * MSI capability is the 1st capability in capability config */ pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_MSI, 0); if (pos != 0 && kvm_check_extension(kvm_state, KVM_CAP_ASSIGN_DEV_IRQ)) { - dev->cap.available |= ASSIGNED_DEVICE_CAP_MSI; - /* Only 32-bit/no-mask currently supported */ - if ((ret = pci_add_capability(pci_dev, PCI_CAP_ID_MSI, pos, 10)) < 0) { + uint16_t flags = pci_get_word(pci_dev->config + pos + PCI_MSI_FLAGS); + + /* Note: KVM does not support multiple messages */ + ret = msi_init(pci_dev, pos, 1, flags & PCI_MSI_FLAGS_64BIT, + flags & PCI_MSI_FLAGS_MASKBIT); + if (ret < 0) { + return ret; + } + ret = msi_set_config_notifiers(pci_dev, assigned_dev_update_msi, + assigned_dev_update_msi_vector); + if (ret < 0) { return ret; } - pci_dev->msi_cap = pos; - - pci_set_word(pci_dev->config + pos + PCI_MSI_FLAGS, - pci_get_word(pci_dev->config + pos + PCI_MSI_FLAGS) & - PCI_MSI_FLAGS_QMASK); - pci_set_long(pci_dev->config + pos + PCI_MSI_ADDRESS_LO, 0); - pci_set_word(pci_dev->config + pos + PCI_MSI_DATA_32, 0); - - /* Set writable fields */ - pci_set_word(pci_dev->wmask + pos + PCI_MSI_FLAGS, - PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE); - pci_set_long(pci_dev->wmask + pos + PCI_MSI_ADDRESS_LO, 0xfffffffc); - pci_set_word(pci_dev->wmask + pos + PCI_MSI_DATA_32, 0xffff); - - dev->dev.msi_cache = g_malloc0(sizeof(MSIRoutingCache)); } /* Expose MSI-X capability */ pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_MSIX, 0); -- 1.7.3.4