From: Jan Kiszka <jan.kiszka@xxxxxxxxxxx> Vector notifiers shall be triggered by the MSI/MSI-X core whenever a relevant configuration change is programmed by the guest. In case of MSI-X, changes are reported when the effective mask (global && per-vector) alters its state. On unmask, the current vector configuration is included in the event report. This allows users - e.g. virtio-pci layer - to transfer this information to external MSI-X routing subsystems - like vhost + KVM in-kernel irqchip. This implementation only provides MSI-X support, but extension to MSI is feasible and will be provided later on when adding support for KVM PCI device assignment. Signed-off-by: Jan Kiszka <jan.kiszka@xxxxxxxxxxx> Signed-off-by: Avi Kivity <avi@xxxxxxxxxx> --- hw/msix.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/msix.h | 4 +++ hw/pci.h | 8 ++++++ 3 files changed, 105 insertions(+) diff --git a/hw/msix.c b/hw/msix.c index e1a7d92..1622e16 100644 --- a/hw/msix.c +++ b/hw/msix.c @@ -139,13 +139,34 @@ static bool msix_is_masked(PCIDevice *dev, int vector) return msix_vector_masked(dev, vector, dev->msix_function_masked); } +static void msix_fire_vector_notifier(PCIDevice *dev, + unsigned int vector, bool is_masked) +{ + MSIMessage msg; + int ret; + + if (!dev->msix_vector_use_notifier) { + return; + } + if (is_masked) { + dev->msix_vector_release_notifier(dev, vector); + } else { + msg = msix_get_message(dev, vector); + ret = dev->msix_vector_use_notifier(dev, vector, msg); + assert(ret >= 0); + } +} + static void msix_handle_mask_update(PCIDevice *dev, int vector, bool was_masked) { bool is_masked = msix_is_masked(dev, vector); + if (is_masked == was_masked) { return; } + msix_fire_vector_notifier(dev, vector, is_masked); + if (!is_masked && msix_is_pending(dev, vector)) { msix_clr_pending(dev, vector); msix_notify(dev, vector); @@ -330,6 +351,7 @@ void msix_save(PCIDevice *dev, QEMUFile *f) void msix_load(PCIDevice *dev, QEMUFile *f) { unsigned n = dev->msix_entries_nr; + unsigned int vector; if (!(dev->cap_present & QEMU_PCI_CAP_MSIX)) { return; @@ -339,6 +361,10 @@ void msix_load(PCIDevice *dev, QEMUFile *f) qemu_get_buffer(f, dev->msix_table_page, n * PCI_MSIX_ENTRY_SIZE); qemu_get_buffer(f, dev->msix_table_page + MSIX_PAGE_PENDING, (n + 7) / 8); msix_update_function_masked(dev); + + for (vector = 0; vector < n; vector++) { + msix_handle_mask_update(dev, vector, true); + } } /* Does device support MSI-X? */ @@ -425,3 +451,70 @@ void msix_unuse_all_vectors(PCIDevice *dev) return; msix_free_irq_entries(dev); } + +static int msix_set_notifier_for_vector(PCIDevice *dev, unsigned int vector) +{ + MSIMessage msg; + + if (msix_is_masked(dev, vector)) { + return 0; + } + msg = msix_get_message(dev, vector); + return dev->msix_vector_use_notifier(dev, vector, msg); +} + +static void msix_unset_notifier_for_vector(PCIDevice *dev, unsigned int vector) +{ + if (msix_is_masked(dev, vector)) { + return; + } + dev->msix_vector_release_notifier(dev, vector); +} + +int msix_set_vector_notifiers(PCIDevice *dev, + MSIVectorUseNotifier use_notifier, + MSIVectorReleaseNotifier release_notifier) +{ + int vector, ret; + + assert(use_notifier && release_notifier); + + dev->msix_vector_use_notifier = use_notifier; + dev->msix_vector_release_notifier = release_notifier; + + if ((dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] & + (MSIX_ENABLE_MASK | MSIX_MASKALL_MASK)) == MSIX_ENABLE_MASK) { + for (vector = 0; vector < dev->msix_entries_nr; vector++) { + ret = msix_set_notifier_for_vector(dev, vector); + if (ret < 0) { + goto undo; + } + } + } + return 0; + +undo: + while (--vector >= 0) { + msix_unset_notifier_for_vector(dev, vector); + } + dev->msix_vector_use_notifier = NULL; + dev->msix_vector_release_notifier = NULL; + return ret; +} + +void msix_unset_vector_notifiers(PCIDevice *dev) +{ + int vector; + + assert(dev->msix_vector_use_notifier && + dev->msix_vector_release_notifier); + + if ((dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] & + (MSIX_ENABLE_MASK | MSIX_MASKALL_MASK)) == MSIX_ENABLE_MASK) { + for (vector = 0; vector < dev->msix_entries_nr; vector++) { + msix_unset_notifier_for_vector(dev, vector); + } + } + dev->msix_vector_use_notifier = NULL; + dev->msix_vector_release_notifier = NULL; +} diff --git a/hw/msix.h b/hw/msix.h index 5aba22b..f33f18b 100644 --- a/hw/msix.h +++ b/hw/msix.h @@ -29,4 +29,8 @@ void msix_notify(PCIDevice *dev, unsigned vector); void msix_reset(PCIDevice *dev); +int msix_set_vector_notifiers(PCIDevice *dev, + MSIVectorUseNotifier use_notifier, + MSIVectorReleaseNotifier release_notifier); +void msix_unset_vector_notifiers(PCIDevice *dev); #endif diff --git a/hw/pci.h b/hw/pci.h index 8d0aa49..c3cacce 100644 --- a/hw/pci.h +++ b/hw/pci.h @@ -173,6 +173,10 @@ typedef struct PCIDeviceClass { const char *romfile; } PCIDeviceClass; +typedef int (*MSIVectorUseNotifier)(PCIDevice *dev, unsigned int vector, + MSIMessage msg); +typedef void (*MSIVectorReleaseNotifier)(PCIDevice *dev, unsigned int vector); + struct PCIDevice { DeviceState qdev; /* PCI config space */ @@ -243,6 +247,10 @@ struct PCIDevice { bool has_rom; MemoryRegion rom; uint32_t rom_bar; + + /* MSI-X notifiers */ + MSIVectorUseNotifier msix_vector_use_notifier; + MSIVectorReleaseNotifier msix_vector_release_notifier; }; void pci_register_bar(PCIDevice *pci_dev, int region_num, -- 1.7.10.1 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html