We're currently using a counter to track the most recent GSI we've
handed out.  This quickly hits KVM_MAX_IRQ_ROUTES when using device
assignment with a driver that regularly toggles the MSI enable bit.
This can mean only a few minutes of usable run time.  Instead, track
used GSIs in a bitmap.

Signed-off-by: Alex Williamson <alex.william...@hp.com>
---

 v2: Added mutex to protect gsi bitmap
 v3: Updated for comments from Michael Tsirkin
     No longer depends on "[PATCH] kvm: device-assignment: Catch GSI overflow"
 v4: Fix gsi_bytes calculation noted by Sheng Yang

 hw/device-assignment.c  |    4 ++
 kvm/libkvm/kvm-common.h |    4 ++
 kvm/libkvm/libkvm.c     |   81 +++++++++++++++++++++++++++++++++++++++++------
 kvm/libkvm/libkvm.h     |   10 ++++++
 4 files changed, 86 insertions(+), 13 deletions(-)

diff --git a/hw/device-assignment.c b/hw/device-assignment.c
index a7365c8..a6cc9b9 100644
--- a/hw/device-assignment.c
+++ b/hw/device-assignment.c
@@ -561,8 +561,10 @@ static void free_dev_irq_entries(AssignedDevice *dev)
 {
     int i;
 
-    for (i = 0; i < dev->irq_entries_nr; i++)
+    for (i = 0; i < dev->irq_entries_nr; i++) {
         kvm_del_routing_entry(kvm_context, &dev->entry[i]);
+        kvm_free_irq_route_gsi(kvm_context, dev->entry[i].gsi);
+    }
     free(dev->entry);
     dev->entry = NULL;
     dev->irq_entries_nr = 0;
diff --git a/kvm/libkvm/kvm-common.h b/kvm/libkvm/kvm-common.h
index 591fb53..4b3cb51 100644
--- a/kvm/libkvm/kvm-common.h
+++ b/kvm/libkvm/kvm-common.h
@@ -66,8 +66,10 @@ struct kvm_context {
 #ifdef KVM_CAP_IRQ_ROUTING
        struct kvm_irq_routing *irq_routes;
        int nr_allocated_irq_routes;
+       void *used_gsi_bitmap;
+       int max_gsi;
+       pthread_mutex_t gsi_mutex;
 #endif
-       int max_used_gsi;
 };
 
 int kvm_alloc_kernel_memory(kvm_context_t kvm, unsigned long memory,
diff --git a/kvm/libkvm/libkvm.c b/kvm/libkvm/libkvm.c
index ba0a5d1..3eaa120 100644
--- a/kvm/libkvm/libkvm.c
+++ b/kvm/libkvm/libkvm.c
@@ -35,6 +35,7 @@
 #include <errno.h>
 #include <sys/ioctl.h>
 #include <inttypes.h>
+#include <pthread.h>
 #include "libkvm.h"
 
 #if defined(__x86_64__) || defined(__i386__)
@@ -65,6 +66,8 @@
 int kvm_abi = EXPECTED_KVM_API_VERSION;
 int kvm_page_size;
 
+static inline void set_bit(uint32_t *buf, unsigned int bit);
+
 struct slot_info {
        unsigned long phys_addr;
        unsigned long len;
@@ -286,6 +289,9 @@ kvm_context_t kvm_init(struct kvm_callbacks *callbacks,
        int fd;
        kvm_context_t kvm;
        int r;
+#ifdef KVM_CAP_IRQ_ROUTING
+       int gsi_count, gsi_bytes, i;
+#endif
 
        fd = open("/dev/kvm", O_RDWR);
        if (fd == -1) {
@@ -322,6 +328,25 @@ kvm_context_t kvm_init(struct kvm_callbacks *callbacks,
        kvm->dirty_pages_log_all = 0;
        kvm->no_irqchip_creation = 0;
        kvm->no_pit_creation = 0;
+#ifdef KVM_CAP_IRQ_ROUTING
+       pthread_mutex_init(&kvm->gsi_mutex, NULL);
+
+       gsi_count = kvm_get_gsi_count(kvm);
+       /* Round up so we can search ints using ffs */
+       gsi_bytes = ((gsi_count + 31) / 32) * 4;
+       kvm->used_gsi_bitmap = malloc(gsi_bytes);
+       if (!kvm->used_gsi_bitmap)
+               goto out_close;
+       memset(kvm->used_gsi_bitmap, 0, gsi_bytes);
+       kvm->max_gsi = gsi_bytes * 8;
+
+       /* Mark all the IOAPIC pin GSIs and any over-allocated
+        * GSIs as already in use. */
+       for (i = 0; i < KVM_IOAPIC_NUM_PINS; i++)
+               set_bit(kvm->used_gsi_bitmap, i);
+       for (i = gsi_count; i < kvm->max_gsi; i++)
+               set_bit(kvm->used_gsi_bitmap, i);
+#endif
 
        return kvm;
  out_close:
@@ -1298,8 +1323,6 @@ int kvm_add_routing_entry(kvm_context_t kvm,
        new->flags = entry->flags;
        new->u = entry->u;
 
-       if (entry->gsi > kvm->max_used_gsi)
-               kvm->max_used_gsi = entry->gsi;
        return 0;
 #else
        return -ENOSYS;
@@ -1404,18 +1427,54 @@ int kvm_commit_irq_routes(kvm_context_t kvm)
 #endif
 }
 
+#ifdef KVM_CAP_IRQ_ROUTING
+static inline void set_bit(uint32_t *buf, unsigned int bit)
+{
+       buf[bit / 32] |= 1U << (bit % 32);
+}
+
+static inline void clear_bit(uint32_t *buf, unsigned int bit)
+{
+       buf[bit / 32] &= ~(1U << (bit % 32));
+}
+
+static int kvm_find_free_gsi(kvm_context_t kvm)
+{
+       int i, bit, gsi;
+       uint32_t *buf = kvm->used_gsi_bitmap;
+
+       for (i = 0; i < kvm->max_gsi / 32; i++) {
+               bit = ffs(~buf[i]);
+               if (!bit)
+                       continue;
+
+               gsi = bit - 1 + i * 32;
+               set_bit(buf, gsi);
+               return gsi;
+       }
+
+       return -ENOSPC;
+}
+#endif
+
 int kvm_get_irq_route_gsi(kvm_context_t kvm)
 {
+       int gsi = -ENOSYS;
+
 #ifdef KVM_CAP_IRQ_ROUTING
-       if (kvm->max_used_gsi >= KVM_IOAPIC_NUM_PINS)  {
-           if (kvm->max_used_gsi <= kvm_get_gsi_count(kvm))
-                return kvm->max_used_gsi + 1;
-            else
-                return -ENOSPC;
-        } else
-            return KVM_IOAPIC_NUM_PINS;
-#else
-       return -ENOSYS;
+       pthread_mutex_lock(&kvm->gsi_mutex);
+       gsi = kvm_find_free_gsi(kvm);
+       pthread_mutex_unlock(&kvm->gsi_mutex);
+#endif
+       return gsi;
+}
+ 
+void kvm_free_irq_route_gsi(kvm_context_t kvm, int gsi)
+{
+#ifdef KVM_CAP_IRQ_ROUTING
+       pthread_mutex_lock(&kvm->gsi_mutex);
+       clear_bit(kvm->used_gsi_bitmap, gsi);
+       pthread_mutex_unlock(&kvm->gsi_mutex);
 #endif
 }
 
diff --git a/kvm/libkvm/libkvm.h b/kvm/libkvm/libkvm.h
index 4821a1e..845bb8a 100644
--- a/kvm/libkvm/libkvm.h
+++ b/kvm/libkvm/libkvm.h
@@ -856,6 +856,16 @@ int kvm_commit_irq_routes(kvm_context_t kvm);
  */
 int kvm_get_irq_route_gsi(kvm_context_t kvm);
 
+/*!
+ * \brief Free used GSI number
+ *
+ * Free used GSI number acquired from kvm_get_irq_route_gsi()
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param gsi GSI number to free
+ */
+void kvm_free_irq_route_gsi(kvm_context_t kvm, int gsi);
+
 #ifdef KVM_CAP_DEVICE_MSIX
 int kvm_assign_set_msix_nr(kvm_context_t kvm,
                           struct kvm_assigned_msix_nr *msix_nr);

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

Reply via email to