mirror of https://github.com/xemu-project/xemu.git
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
8
hw/pci.h
8
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,
|
||||
|
|
Loading…
Reference in New Issue