msix: Introduce vector notifiers
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@siemens.com> Signed-off-by: Avi Kivity <avi@redhat.com>
This commit is contained in:
parent
5b5f1330da
commit
2cdfe53c5f
93
hw/msix.c
93
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);
|
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)
|
static void msix_handle_mask_update(PCIDevice *dev, int vector, bool was_masked)
|
||||||
{
|
{
|
||||||
bool is_masked = msix_is_masked(dev, vector);
|
bool is_masked = msix_is_masked(dev, vector);
|
||||||
|
|
||||||
if (is_masked == was_masked) {
|
if (is_masked == was_masked) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
msix_fire_vector_notifier(dev, vector, is_masked);
|
||||||
|
|
||||||
if (!is_masked && msix_is_pending(dev, vector)) {
|
if (!is_masked && msix_is_pending(dev, vector)) {
|
||||||
msix_clr_pending(dev, vector);
|
msix_clr_pending(dev, vector);
|
||||||
msix_notify(dev, vector);
|
msix_notify(dev, vector);
|
||||||
|
@ -330,6 +351,7 @@ void msix_save(PCIDevice *dev, QEMUFile *f)
|
||||||
void msix_load(PCIDevice *dev, QEMUFile *f)
|
void msix_load(PCIDevice *dev, QEMUFile *f)
|
||||||
{
|
{
|
||||||
unsigned n = dev->msix_entries_nr;
|
unsigned n = dev->msix_entries_nr;
|
||||||
|
unsigned int vector;
|
||||||
|
|
||||||
if (!(dev->cap_present & QEMU_PCI_CAP_MSIX)) {
|
if (!(dev->cap_present & QEMU_PCI_CAP_MSIX)) {
|
||||||
return;
|
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, n * PCI_MSIX_ENTRY_SIZE);
|
||||||
qemu_get_buffer(f, dev->msix_table_page + MSIX_PAGE_PENDING, (n + 7) / 8);
|
qemu_get_buffer(f, dev->msix_table_page + MSIX_PAGE_PENDING, (n + 7) / 8);
|
||||||
msix_update_function_masked(dev);
|
msix_update_function_masked(dev);
|
||||||
|
|
||||||
|
for (vector = 0; vector < n; vector++) {
|
||||||
|
msix_handle_mask_update(dev, vector, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Does device support MSI-X? */
|
/* Does device support MSI-X? */
|
||||||
|
@ -425,3 +451,70 @@ void msix_unuse_all_vectors(PCIDevice *dev)
|
||||||
return;
|
return;
|
||||||
msix_free_irq_entries(dev);
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -29,4 +29,8 @@ void msix_notify(PCIDevice *dev, unsigned vector);
|
||||||
|
|
||||||
void msix_reset(PCIDevice *dev);
|
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
|
#endif
|
||||||
|
|
8
hw/pci.h
8
hw/pci.h
|
@ -173,6 +173,10 @@ typedef struct PCIDeviceClass {
|
||||||
const char *romfile;
|
const char *romfile;
|
||||||
} PCIDeviceClass;
|
} PCIDeviceClass;
|
||||||
|
|
||||||
|
typedef int (*MSIVectorUseNotifier)(PCIDevice *dev, unsigned int vector,
|
||||||
|
MSIMessage msg);
|
||||||
|
typedef void (*MSIVectorReleaseNotifier)(PCIDevice *dev, unsigned int vector);
|
||||||
|
|
||||||
struct PCIDevice {
|
struct PCIDevice {
|
||||||
DeviceState qdev;
|
DeviceState qdev;
|
||||||
/* PCI config space */
|
/* PCI config space */
|
||||||
|
@ -243,6 +247,10 @@ struct PCIDevice {
|
||||||
bool has_rom;
|
bool has_rom;
|
||||||
MemoryRegion rom;
|
MemoryRegion rom;
|
||||||
uint32_t rom_bar;
|
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,
|
void pci_register_bar(PCIDevice *pci_dev, int region_num,
|
||||||
|
|
Loading…
Reference in a new issue