From 53705b5f7d6100005047ae99fb9eaa29678ac4c6 Mon Sep 17 00:00:00 2001
From: Michael S. Tsirkin <mst@redhat.com>
Date: Wed, 19 Jan 2011 11:49:33 -0200
Subject: [PATCH 11/24] kvm: test for ioeventfd support on old kernels

RH-Author: Michael S. Tsirkin <mst@redhat.com>
Message-id: <36fb20341761480bee19877d25949bc11c3219d0.1295437447.git.mst@redhat.com>
Patchwork-id: 16558
O-Subject: [PATCHv4 RHEL6.1 4/8] kvm: test for ioeventfd support on old kernels
Bugzilla: 633394
RH-Acked-by: Christoph Hellwig <chellwig@redhat.com>
RH-Acked-by: Juan Quintela <quintela@redhat.com>
RH-Acked-by: Alex Williamson <alex.williamson@redhat.com>
RH-Acked-by: Jes Sorensen <Jes.Sorensen@redhat.com>

commit d2f2b8a740c82319f9eea51ebed50815fbc3da3e
Author: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
Date:   Mon Jan 10 13:50:05 2011 +0200

    kvm: test for ioeventfd support on old kernels

    There used to be a limit of 6 KVM io bus devices in the kernel.
    On such a kernel, we can't use many ioeventfds for host notification
    since the limit is reached too easily.

    Add an API to test for this condition.

    Signed-off-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
    Signed-off-by: Michael S. Tsirkin <mst@redhat.com>

Note: folded in https://patchwork.kernel.org/patch/400592/
      to make it build on qemu-kvm.
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=633394
---
 kvm-all.c  |   51 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 kvm.h      |    8 ++++++++
 qemu-kvm.c |    2 ++
 qemu-kvm.h |    1 +
 4 files changed, 61 insertions(+), 1 deletions(-)

Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
---
 kvm-all.c  |   51 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 kvm.h      |    8 ++++++++
 qemu-kvm.c |    2 ++
 qemu-kvm.h |    1 +
 4 files changed, 61 insertions(+), 1 deletions(-)

diff --git a/kvm-all.c b/kvm-all.c
index 6506e90..fe03358 100644
--- a/kvm-all.c
+++ b/kvm-all.c
@@ -26,6 +26,11 @@
 #include "gdbstub.h"
 #include "kvm.h"
 
+/* This check must be after config-host.h is included */
+#ifdef CONFIG_EVENTFD
+#include <sys/eventfd.h>
+#endif
+
 #ifdef KVM_UPSTREAM
 /* KVM uses PAGE_SIZE in it's definition of COALESCED_MMIO_MAX */
 #define PAGE_SIZE TARGET_PAGE_SIZE
@@ -67,6 +72,7 @@ struct KVMState
 #endif
     int irqchip_in_kernel;
     int pit_in_kernel;
+    int many_ioeventfds;
 };
 
 static KVMState *kvm_state;
@@ -396,8 +402,41 @@ int kvm_check_extension(KVMState *s, unsigned int extension)
 
     return ret;
 }
-#ifdef KVM_UPSTREAM
 
+int kvm_check_many_ioeventfds(void)
+{
+    /* Older kernels have a 6 device limit on the KVM io bus.  Find out so we
+     * can avoid creating too many ioeventfds.
+     */
+#ifdef CONFIG_EVENTFD
+    int ioeventfds[7];
+    int i, ret = 0;
+    for (i = 0; i < ARRAY_SIZE(ioeventfds); i++) {
+        ioeventfds[i] = eventfd(0, EFD_CLOEXEC);
+        if (ioeventfds[i] < 0) {
+            break;
+        }
+        ret = kvm_set_ioeventfd_pio_word(ioeventfds[i], 0, i, true);
+        if (ret < 0) {
+            close(ioeventfds[i]);
+            break;
+        }
+    }
+
+    /* Decide whether many devices are supported or not */
+    ret = i == ARRAY_SIZE(ioeventfds);
+
+    while (i-- > 0) {
+        kvm_set_ioeventfd_pio_word(ioeventfds[i], 0, i, false);
+        close(ioeventfds[i]);
+    }
+    return ret;
+#else
+    return 0;
+#endif
+}
+
+#ifdef KVM_UPSTREAM
 int kvm_init(int smp_cpus)
 {
     static const char upgrade_note[] =
@@ -495,6 +534,8 @@ int kvm_init(int smp_cpus)
 
     kvm_state = s;
 
+    s->many_ioeventfds = kvm_check_many_ioeventfds();
+
     return 0;
 
 err:
@@ -886,6 +927,14 @@ int kvm_has_vcpu_events(void)
     return kvm_state->vcpu_events;
 }
 
+int kvm_has_many_ioeventfds(void)
+{
+    if (!kvm_enabled()) {
+        return 0;
+    }
+    return kvm_state->many_ioeventfds;
+}
+
 #ifdef KVM_UPSTREAM
 void kvm_setup_guest_memory(void *start, size_t size)
 {
diff --git a/kvm.h b/kvm.h
index 47fc819..d93b20b 100644
--- a/kvm.h
+++ b/kvm.h
@@ -156,14 +156,22 @@ static inline void cpu_synchronize_state(CPUState *env)
 
 #endif
 
+int kvm_has_many_ioeventfds(void);
+
 #if defined(KVM_IOEVENTFD) && defined(CONFIG_KVM)
 int kvm_set_ioeventfd_pio_word(int fd, uint16_t adr, uint16_t val, bool assign);
+int kvm_check_many_ioeventfds(void);
 #else
 static inline
 int kvm_set_ioeventfd_pio_word(int fd, uint16_t adr, uint16_t val, bool assign)
 {
     return -ENOSYS;
 }
+static inline
+int kvm_check_many_ioeventfds(void)
+{
+    return 0;
+}
 #endif
 
 #if defined(KVM_IRQFD) && defined(CONFIG_KVM)
diff --git a/qemu-kvm.c b/qemu-kvm.c
index 90d5714..f06408b 100644
--- a/qemu-kvm.c
+++ b/qemu-kvm.c
@@ -2238,6 +2238,8 @@ static int kvm_create_context(void)
     kvm_state->vcpu_events = kvm_check_extension(kvm_state, KVM_CAP_VCPU_EVENTS);
 #endif
 
+    kvm_state->many_ioeventfds = kvm_check_many_ioeventfds();
+
     kvm_init_ap();
     if (kvm_irqchip) {
         if (!qemu_kvm_has_gsi_routing()) {
diff --git a/qemu-kvm.h b/qemu-kvm.h
index 6b3e5a1..1b905f8 100644
--- a/qemu-kvm.h
+++ b/qemu-kvm.h
@@ -1151,6 +1151,7 @@ typedef struct KVMState {
     QTAILQ_HEAD(, kvm_sw_breakpoint) kvm_sw_breakpoints;
 #endif
     int irqchip_in_kernel;
+    int many_ioeventfds;
 
     struct kvm_context kvm_context;
 } KVMState;
-- 
1.7.4.rc1.16.gd2f15e

