@@ -411,6 +411,88 @@ fail_irqfd:
return;
}
+/*
+ * Functions used with forwarding capability
+ */
+
+static bool has_kvm_vfio_forward_capability(void)
+{
+ struct kvm_device_attr attr = {
+ .group = KVM_DEV_VFIO_DEVICE,
+ .attr = KVM_DEV_VFIO_DEVICE_FORWARD_IRQ};
+
+ if (ioctl(vfio_kvm_device_fd, KVM_HAS_DEVICE_ATTR, &attr) == 0) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static void vfio_start_forward_injection(SysBusDevice *sbdev, qemu_irq irq)
+{
+ VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev);
+ VFIOINTp *intp;
+ bool found = false;
+ struct kvm_device_attr attr = {
+ .group = KVM_DEV_VFIO_DEVICE,
+ .attr = KVM_DEV_VFIO_DEVICE_FORWARD_IRQ};
+
+ QLIST_FOREACH(intp, &vdev->intp_list, next) {
+ if (intp->qemuirq == irq) {
+ found = true;
+ break;
+ }
+ }
+ assert(found);
+
+ if (intp->forwarded) {
+ return;
+ }
+
+ /*
+ * stop VFIO signaling and unmask the physical IRQ since
+ * forwarding cannot be set if the IRQ is active or vfio masked
+ */
+ vfio_disable_irqindex(&intp->vdev->vbasedev, intp->pin);
+ vfio_unmask_single_irqindex(&intp->vdev->vbasedev, intp->pin);
+
+ kvm_vfio_get_device_irq(kvm_state, intp->vdev->vbasedev.fd,
+ intp->pin, 0, 1, intp->qemuirq, &intp->fwd_irq);
+
+ attr.addr = (uint64_t)(unsigned long)intp->fwd_irq;
+
+ if (ioctl(vfio_kvm_device_fd, KVM_SET_DEVICE_ATTR, &attr) < 0) {
+ error_report("vfio: failed to forward irq %d, do standard irqfd",
+ intp->pin);
+ kvm_vfio_put_device_irq(intp->fwd_irq);
+ } else {
+ trace_vfio_platform_start_fwd_injection(intp->pin);
+ intp->forwarded = true;
+ }
+
+ if (kvm_irqchip_add_irqfd_notifier(kvm_state, &intp->interrupt,
+ &intp->unmask, irq) < 0) {
+ goto fail_irqfd;
+ }
+
+ if (vfio_set_trigger_eventfd(intp, NULL) < 0) {
+ goto fail_vfio;
+ }
+ /* only used if forwarding setup failed */
+ if (vfio_set_resample_eventfd(intp) < 0) {
+ goto fail_vfio;
+ }
+
+ intp->kvm_accel = true;
+ return;
+fail_vfio:
+ kvm_irqchip_remove_irqfd_notifier(kvm_state, &intp->interrupt, irq);
+fail_irqfd:
+ vfio_start_eventfd_injection(intp);
+ vfio_unmask_single_irqindex(&vdev->vbasedev, intp->pin);
+ return;
+}
+
#endif /* CONFIG_KVM */
/* VFIO skeleton */
@@ -655,13 +737,6 @@ static void vfio_platform_realize(DeviceState *dev, Error **errp)
vbasedev->type = VFIO_DEVICE_TYPE_PLATFORM;
vbasedev->ops = &vfio_platform_ops;
-#ifdef CONFIG_KVM
- if (kvm_irqfds_enabled() && kvm_resamplefds_enabled() &&
- vdev->irqfd_allowed) {
- sbc->connect_irq_notifier = vfio_start_irqfd_injection;
- }
-#endif
-
trace_vfio_platform_realize(vbasedev->name, vdev->compat);
ret = vfio_base_device_init(vbasedev);
@@ -679,6 +754,19 @@ static void vfio_platform_realize(DeviceState *dev, Error **errp)
QLIST_FOREACH(intp, &vdev->intp_list, next) {
vfio_start_eventfd_injection(intp);
}
+
+#ifdef CONFIG_KVM
+ if (kvm_irqfds_enabled() && kvm_resamplefds_enabled() &&
+ vdev->irqfd_allowed) {
+ if (has_kvm_vfio_forward_capability() &&
+ vdev->forward_allowed) {
+ sbc->connect_irq_notifier = vfio_start_forward_injection;
+ } else {
+ sbc->connect_irq_notifier = vfio_start_irqfd_injection;
+ }
+ }
+#endif
+
}
static const VMStateDescription vfio_platform_vmstate = {
@@ -692,6 +780,7 @@ static Property vfio_platform_dev_properties[] = {
DEFINE_PROP_UINT32("mmap-timeout-ms", VFIOPlatformDevice,
mmap_timeout, 1100),
DEFINE_PROP_BOOL("x-irqfd", VFIOPlatformDevice, irqfd_allowed, true),
+ DEFINE_PROP_BOOL("x-forward", VFIOPlatformDevice, forward_allowed, true),
DEFINE_PROP_END_OF_LIST(),
};
@@ -42,6 +42,8 @@ typedef struct VFIOINTp {
uint8_t pin; /* index */
uint32_t flags; /* IRQ info flags */
bool kvm_accel; /* set when QEMU bypass through KVM enabled */
+ struct kvm_vfio_dev_irq *fwd_irq;
+ bool forwarded;
} VFIOINTp;
/* function type for user side eventfd handler */
@@ -59,6 +61,7 @@ typedef struct VFIOPlatformDevice {
QEMUTimer *mmap_timer; /* allows fast-path resume after IRQ hit */
QemuMutex intp_mutex; /* protect the intp_list IRQ state */
bool irqfd_allowed; /* debug option to force irqfd on/off */
+ bool forward_allowed; /* debug option to force forwarding on/off */
} VFIOPlatformDevice;
typedef struct VFIOPlatformDeviceClass {
@@ -1572,6 +1572,7 @@ vfio_platform_intp_inject_pending_lockheld(int pin, int fd) "Inject pending IRQ
vfio_platform_populate_interrupts(int pin, int count, int flags) "- IRQ index %d: count %d, flags=0x%x"
vfio_intp_interrupt_set_pending(int index) "irq %d is set PENDING"
vfio_platform_start_irqfd_injection(int index, int fd, int resamplefd) "IRQ index=%d, fd = %d, resamplefd = %d"
+vfio_platform_start_fwd_injection(int pin) "forwarding set for IRQ pin %d"
#hw/acpi/memory_hotplug.c
mhp_acpi_invalid_slot_selected(uint32_t slot) "0x%"PRIx32