To support VFIO based device assignment, we need to be able to get
an EOI out of the KVM irqchip.  This introduces a mechanism to do
that by registering an eventfd to be signaled when the IRQ is ACKed.

Signed-off-by: Alex Williamson <[email protected]>
---

 include/linux/kvm.h      |   13 ++++++
 include/linux/kvm_host.h |    6 +++
 virt/kvm/eventfd.c       |   95 ++++++++++++++++++++++++++++++++++++++++++++++
 virt/kvm/kvm_main.c      |    8 ++++
 4 files changed, 122 insertions(+), 0 deletions(-)

diff --git a/include/linux/kvm.h b/include/linux/kvm.h
index ea2dc1a..92d5b27 100644
--- a/include/linux/kvm.h
+++ b/include/linux/kvm.h
@@ -541,6 +541,7 @@ struct kvm_ppc_pvinfo {
 #define KVM_CAP_PPC_GET_PVINFO 57
 #define KVM_CAP_PPC_IRQ_LEVEL 58
 #define KVM_CAP_ASYNC_PF 59
+#define KVM_CAP_EOI_EVENTFD 60
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
@@ -620,6 +621,16 @@ struct kvm_clock_data {
        __u32 pad[9];
 };
 
+#define KVM_EOI_EVENTFD_FLAG_DEASSIGN (1 << 0)
+#define KVM_EOI_EVENTFD_FLAG_DEASSERT (1 << 1)
+
+struct kvm_eoi {
+       __u32 fd;
+       __u32 gsi;
+       __u32 flags;
+       __u8  pad[20];
+};
+
 /*
  * ioctls for VM fds
  */
@@ -677,6 +688,8 @@ struct kvm_clock_data {
 #define KVM_SET_PIT2              _IOW(KVMIO,  0xa0, struct kvm_pit_state2)
 /* Available with KVM_CAP_PPC_GET_PVINFO */
 #define KVM_PPC_GET_PVINFO       _IOW(KVMIO,  0xa1, struct kvm_ppc_pvinfo)
+/* Available with KVM_CAP_EOI_EVENTFD */
+#define KVM_EOI_EVENTFD           _IOW(KVMIO,  0xa2, struct kvm_eoi)
 
 /*
  * ioctls for vcpu fds
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index ee4314e..5d50a7e 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -227,6 +227,7 @@ struct kvm {
                struct list_head  items;
        } irqfds;
        struct list_head ioeventfds;
+       struct list_head eoi_eventfds;
 #endif
        struct kvm_vm_stat stat;
        struct kvm_arch arch;
@@ -643,6 +644,7 @@ void kvm_eventfd_init(struct kvm *kvm);
 int kvm_irqfd(struct kvm *kvm, int fd, int gsi, int flags);
 void kvm_irqfd_release(struct kvm *kvm);
 int kvm_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args);
+int kvm_eoi_eventfd(struct kvm *kvm, struct kvm_eoi *eoi);
 
 #else
 
@@ -658,6 +660,10 @@ static inline int kvm_ioeventfd(struct kvm *kvm, struct 
kvm_ioeventfd *args)
        return -ENOSYS;
 }
 
+static inline int kvm_eoi_eventfd(struct kvm *kvm, struct kvm_eoi *eoi)
+{
+       return -ENOSYS;
+}
 #endif /* CONFIG_HAVE_KVM_EVENTFD */
 
 #ifdef CONFIG_KVM_APIC_ARCHITECTURE
diff --git a/virt/kvm/eventfd.c b/virt/kvm/eventfd.c
index c1f1e3c..3dbfb21 100644
--- a/virt/kvm/eventfd.c
+++ b/virt/kvm/eventfd.c
@@ -253,6 +253,7 @@ kvm_eventfd_init(struct kvm *kvm)
        spin_lock_init(&kvm->irqfds.lock);
        INIT_LIST_HEAD(&kvm->irqfds.items);
        INIT_LIST_HEAD(&kvm->ioeventfds);
+       INIT_LIST_HEAD(&kvm->eoi_eventfds);
 }
 
 /*
@@ -586,3 +587,97 @@ kvm_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)
 
        return kvm_assign_ioeventfd(kvm, args);
 }
+
+/*
+ * --------------------------------------------------------------------
+ *  eoi_eventfd: Translate KVM APIC/IOAPIC EOI into eventfd signal.
+ *
+ *  userspace can register GSIs with an eventfd for receiving notification
+ *  when an EOI occurs.
+ * --------------------------------------------------------------------
+ */
+
+struct _eoi_eventfd {
+       struct list_head            list;
+       struct kvm                  *kvm;
+       struct eventfd_ctx          *eventfd;
+       bool                        deassert;
+       struct kvm_irq_ack_notifier notifier;
+};
+
+static void kvm_eoi_eventfd_acked(struct kvm_irq_ack_notifier *notifier)
+{
+       struct _eoi_eventfd *p;
+
+       p = container_of(notifier, struct _eoi_eventfd, notifier);
+
+       if (p->deassert)
+               kvm_set_irq(p->kvm, KVM_USERSPACE_IRQ_SOURCE_ID,
+                           notifier->gsi, 0);
+
+       eventfd_signal(p->eventfd, 1);
+}
+
+static int kvm_assign_eoi_eventfd(struct kvm *kvm, struct kvm_eoi *eoi)
+{
+       struct eventfd_ctx *eventfd;
+       struct _eoi_eventfd *p;
+
+       eventfd = eventfd_ctx_fdget(eoi->fd);
+       if (IS_ERR(eventfd))
+               return PTR_ERR(eventfd);
+
+       p = kzalloc(sizeof(*p), GFP_KERNEL);
+       if (!p) {
+               eventfd_ctx_put(eventfd);
+               return -ENOMEM;
+       }
+
+       INIT_LIST_HEAD(&p->list);
+       p->kvm = kvm;
+       p->eventfd = eventfd;
+       p->deassert = !!(eoi->flags & KVM_EOI_EVENTFD_FLAG_DEASSERT);
+
+       p->notifier.gsi = eoi->gsi;
+       p->notifier.irq_acked = kvm_eoi_eventfd_acked;
+
+       list_add_tail(&p->list, &kvm->eoi_eventfds);
+       kvm_register_irq_ack_notifier(kvm, &p->notifier);
+
+       return 0;
+}
+
+static int kvm_deassign_eoi_eventfd(struct kvm *kvm, struct kvm_eoi *eoi)
+{
+       struct eventfd_ctx *eventfd;
+       struct _eoi_eventfd *p, *tmp;
+       int ret = -ENOENT;
+
+       eventfd = eventfd_ctx_fdget(eoi->fd);
+       if (IS_ERR(eventfd))
+               return PTR_ERR(eventfd);
+
+       list_for_each_entry_safe(p, tmp, &kvm->eoi_eventfds, list) {
+               if (p->eventfd != eventfd || p->notifier.gsi != eoi->gsi)
+                       continue;
+
+               kvm_unregister_irq_ack_notifier(kvm, &p->notifier);
+               eventfd_ctx_put(p->eventfd);
+               list_del(&p->list);
+               kfree(p);
+               ret = 0;
+               break;
+       }
+
+       eventfd_ctx_put(eventfd);
+
+       return ret;
+}
+
+int kvm_eoi_eventfd(struct kvm *kvm, struct kvm_eoi *eoi)
+{
+       if (eoi->flags & KVM_EOI_EVENTFD_FLAG_DEASSIGN)
+               return kvm_deassign_eoi_eventfd(kvm, eoi);
+
+       return kvm_assign_eoi_eventfd(kvm, eoi);
+}
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 88d869e..7ca6f13 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -1807,6 +1807,14 @@ static long kvm_vm_ioctl(struct file *filp,
                mutex_unlock(&kvm->lock);
                break;
 #endif
+       case KVM_EOI_EVENTFD: {
+               struct kvm_eoi eoi;
+               r = -EFAULT;
+               if (copy_from_user(&eoi, argp, sizeof eoi))
+                       goto out;
+               r = kvm_eoi_eventfd(kvm, &eoi);
+               break;
+       }
        default:
                r = kvm_arch_vm_ioctl(filp, ioctl, arg);
                if (r == -ENOTTY)

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to