@@ -67,7 +67,8 @@ struct kvm_context {
struct kvm_irq_routing *irq_routes;
int nr_allocated_irq_routes;
#endif
- int max_used_gsi;
+ void *used_gsi_bitmap;
+ int max_gsi;
};
int kvm_alloc_kernel_memory(kvm_context_t kvm, unsigned long memory,
@@ -61,10 +61,32 @@
#define DPRINTF(fmt, args...) do {} while (0)
#endif
+#define MIN(x,y) ((x) < (y) ? (x) : (y))
+#define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1))
int kvm_abi = EXPECTED_KVM_API_VERSION;
int kvm_page_size;
+static inline void set_gsi(kvm_context_t kvm, unsigned int gsi)
+{
+ uint32_t *bitmap = kvm->used_gsi_bitmap;
+
+ if (gsi < kvm->max_gsi)
+ bitmap[gsi / 32] |= 1U << (gsi % 32);
+ else
+ DPRINTF("Invalid GSI %d\n");
+}
+
+static inline void clear_gsi(kvm_context_t kvm, unsigned int gsi)
+{
+ uint32_t *bitmap = kvm->used_gsi_bitmap;
+
+ if (gsi < kvm->max_gsi)
+ bitmap[gsi / 32] &= ~(1U << (gsi % 32));
+ else
+ DPRINTF("Invalid GSI %d\n");
+}
+
struct slot_info {
unsigned long phys_addr;
unsigned long len;
@@ -285,7 +307,7 @@ kvm_context_t kvm_init(struct kvm_callbacks *callbacks,
{
int fd;
kvm_context_t kvm;
- int r;
+ int r, gsi_count;
fd = open("/dev/kvm", O_RDWR);
if (fd == -1) {
@@ -323,6 +345,23 @@ kvm_context_t kvm_init(struct kvm_callbacks *callbacks,
kvm->no_irqchip_creation = 0;
kvm->no_pit_creation = 0;
+ gsi_count = kvm_get_gsi_count(kvm);
+ if (gsi_count > 0) {
+ int gsi_bits, i;
+
+ /* Round up so we can search ints using ffs */
+ gsi_bits = ALIGN(gsi_count, 32);
+ kvm->used_gsi_bitmap = malloc(gsi_bits / 8);
+ if (!kvm->used_gsi_bitmap)
+ goto out_close;
+ memset(kvm->used_gsi_bitmap, 0, gsi_bits / 8);
+ kvm->max_gsi = gsi_bits;
+
+ /* Mark any over-allocated bits as already in use */
+ for (i = gsi_count; i < gsi_bits; i++)
+ set_gsi(kvm, i);
+ }
+
return kvm;
out_close:
close(fd);
@@ -626,9 +665,6 @@ int kvm_get_dirty_pages(kvm_context_t kvm, unsigned long phys_addr, void *buf)
return kvm_get_map(kvm, KVM_GET_DIRTY_LOG, slot, buf);
}
-#define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1))
-#define BITMAP_SIZE(m) (ALIGN(((m)/PAGE_SIZE), sizeof(long) * 8) / 8)
-
int kvm_get_dirty_pages_range(kvm_context_t kvm, unsigned long phys_addr,
unsigned long len, void *buf, void *opaque,
int (*cb)(unsigned long start, unsigned long len,
@@ -1298,8 +1334,8 @@ 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;
+ set_gsi(kvm, entry->gsi);
+
return 0;
#else
return -ENOSYS;
@@ -1327,12 +1363,14 @@ int kvm_del_routing_entry(kvm_context_t kvm,
{
#ifdef KVM_CAP_IRQ_ROUTING
struct kvm_irq_routing_entry *e, *p;
- int i, found = 0;
+ int i, gsi, found = 0;
+
+ gsi = entry->gsi;
for (i = 0; i < kvm->irq_routes->nr; ++i) {
e = &kvm->irq_routes->entries[i];
if (e->type == entry->type
- && e->gsi == entry->gsi) {
+ && e->gsi == gsi) {
switch (e->type)
{
case KVM_IRQ_ROUTING_IRQCHIP: {
@@ -1363,8 +1401,19 @@ int kvm_del_routing_entry(kvm_context_t kvm,
default:
break;
}
- if (found)
+ if (found) {
+ /* If there are no other users of this GSI
+ * mark it available in the bitmap */
+ for (i = 0; i < kvm->irq_routes->nr; i++) {
+ e = &kvm->irq_routes->entries[i];
+ if (e->gsi == gsi)
+ break;
+ }
+ if (i == kvm->irq_routes->nr)
+ clear_gsi(kvm, gsi);
+
return 0;
+ }
}
}
return -ESRCH;
@@ -1406,17 +1455,19 @@ int kvm_commit_irq_routes(kvm_context_t kvm)
int kvm_get_irq_route_gsi(kvm_context_t kvm)
{
-#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;
-#endif
+ int i, bit;
+ uint32_t *buf = kvm->used_gsi_bitmap;
+
+ /* Return the lowest unused GSI in the bitmap */
+ for (i = 0; i < kvm->max_gsi / 32; i++) {
+ bit = ffs(~buf[i]);
+ if (!bit)
+ continue;
+
+ return bit - 1 + i * 32;
+ }
+
+ return -ENOSPC;
}
#ifdef KVM_CAP_DEVICE_MSIX
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 (such as Linux kernels 2.6.21-26). This can mean only a few minutes of usable run time. Instead, track used GSIs in a bitmap. Signed-off-by: Alex Williamson <alex.williamson@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 v5: Remove mutex per Avi Fix negative gsi_count path per Michael Remove KVM_CAP_IRQ_ROUTING per Michael, ppc should still be protected by the KVM_IOAPIC_NUM_PINS check v6: Make use of ALIGN macro, per Michael Define KVM_IOAPIC_NUM_PINS if not already, per Michael Fix comment indent, per Michael Remove unused BITMAP_SIZE macro v7: Don't define KVM_IOAPIC_NUM_PINS, mark bitmap in common paths so we can stay blissfully ignorant of ioapics kvm/libkvm/kvm-common.h | 3 +- kvm/libkvm/libkvm.c | 91 +++++++++++++++++++++++++++++++++++++---------- 2 files changed, 73 insertions(+), 21 deletions(-) -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html