qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [RFC][PATCH v2 2/4] kvm: Introduce basic MSI support for in


From: Jan Kiszka
Subject: [Qemu-devel] [RFC][PATCH v2 2/4] kvm: Introduce basic MSI support for in-kernel irqchips
Date: Tue, 3 Apr 2012 09:23:14 +0200

From: Jan Kiszka <address@hidden>

This patch basically adds kvm_irqchip_send_msi, a service for sending
arbitrary MSI messages to KVM's in-kernel irqchip models.

As the current KVI API requires us to establish a static route from a
pseudo GSI to the target MSI message and inject the MSI via toggling
that GSI, we need to play some tricks to make this unfortunately
interface transparent. We create those routes on demand and keep them
in a hash table. Succeeding messages can then search for an existing
route in the table first and reuse it whenever possible. If we should
run out of limited GSIs, we simply flush the table and rebuild it as
messages are sent.

This approach is rather simple and could be optimized further. However,
future kernels will contain a more efficient MSI injection interface
that will obsolete the GSI-based dynamic injection.

Signed-off-by: Jan Kiszka <address@hidden>
---
 kvm-all.c |  171 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 kvm.h     |    1 +
 2 files changed, 171 insertions(+), 1 deletions(-)

diff --git a/kvm-all.c b/kvm-all.c
index eb0b4c0..5256511 100644
--- a/kvm-all.c
+++ b/kvm-all.c
@@ -48,6 +48,8 @@
     do { } while (0)
 #endif
 
+#define KVM_MSI_HASHTAB_SIZE    256
+
 typedef struct KVMSlot
 {
     target_phys_addr_t start_addr;
@@ -59,6 +61,11 @@ typedef struct KVMSlot
 
 typedef struct kvm_dirty_log KVMDirtyLog;
 
+typedef struct KVMMSIRoute {
+    struct kvm_irq_routing_entry kroute;
+    QTAILQ_ENTRY(KVMMSIRoute) entry;
+} KVMMSIRoute;
+
 struct KVMState
 {
     KVMSlot slots[32];
@@ -87,6 +94,7 @@ struct KVMState
     int nr_allocated_irq_routes;
     uint32_t *used_gsi_bitmap;
     unsigned int gsi_count;
+    QTAILQ_HEAD(msi_hashtab, KVMMSIRoute) msi_hashtab[KVM_MSI_HASHTAB_SIZE];
 #endif
 };
 
@@ -860,9 +868,14 @@ static void set_gsi(KVMState *s, unsigned int gsi)
     s->used_gsi_bitmap[gsi / 32] |= 1U << (gsi % 32);
 }
 
+static void clear_gsi(KVMState *s, unsigned int gsi)
+{
+    s->used_gsi_bitmap[gsi / 32] &= ~(1U << (gsi % 32));
+}
+
 static void kvm_init_irq_routing(KVMState *s)
 {
-    int gsi_count;
+    int gsi_count, i;
 
     gsi_count = kvm_check_extension(s, KVM_CAP_IRQ_ROUTING);
     if (gsi_count > 0) {
@@ -882,6 +895,10 @@ static void kvm_init_irq_routing(KVMState *s)
     s->irq_routes = g_malloc0(sizeof(*s->irq_routes));
     s->nr_allocated_irq_routes = 0;
 
+    for (i = 0; i < KVM_MSI_HASHTAB_SIZE; i++) {
+        QTAILQ_INIT(&s->msi_hashtab[i]);
+    }
+
     kvm_arch_init_irq_routing(s);
 }
 
@@ -912,6 +929,54 @@ static void kvm_add_routing_entry(KVMState *s,
     set_gsi(s, entry->gsi);
 }
 
+static void kvm_remove_routing_entry(KVMState *s,
+                                     struct kvm_irq_routing_entry *entry)
+{
+    struct kvm_irq_routing_entry *e;
+    int gsi = entry->gsi;
+    int i;
+
+    for (i = 0; i < s->irq_routes->nr; ++i) {
+        e = &s->irq_routes->entries[i];
+        if (e->type == entry->type && e->gsi == gsi) {
+            switch (e->type) {
+            case KVM_IRQ_ROUTING_IRQCHIP:
+                if (e->u.irqchip.irqchip == entry->u.irqchip.irqchip &&
+                    e->u.irqchip.pin == entry->u.irqchip.pin) {
+                    goto found;
+                }
+                break;
+            case KVM_IRQ_ROUTING_MSI:
+                if (e->u.msi.address_lo == entry->u.msi.address_lo &&
+                    e->u.msi.address_hi == entry->u.msi.address_hi &&
+                    e->u.msi.data == entry->u.msi.data) {
+                    goto found;
+                }
+                break;
+            default:
+                break;
+            }
+        }
+    }
+    /* route not found */
+    return;
+
+found:
+    s->irq_routes->nr--;
+    *e = s->irq_routes->entries[s->irq_routes->nr];
+
+    /* If there are no other users of this GSI, release it. */
+    for (i = 0; i < s->irq_routes->nr; i++) {
+        e = &s->irq_routes->entries[i];
+        if (e->gsi == gsi) {
+            break;
+        }
+    }
+    if (i == s->irq_routes->nr) {
+        clear_gsi(s, gsi);
+    }
+}
+
 void kvm_irqchip_add_route(KVMState *s, int irq, int irqchip, int pin)
 {
     struct kvm_irq_routing_entry e;
@@ -932,11 +997,115 @@ int kvm_irqchip_commit_routes(KVMState *s)
     return kvm_vm_ioctl(s, KVM_SET_GSI_ROUTING, s->irq_routes);
 }
 
+static unsigned int kvm_hash_msi(uint32_t data)
+{
+    /* This is optimized for IA32 MSI layout. However, no other arch shall
+     * repeat the mistake of not providing a direct MSI injection API. */
+    return data & 0xff;
+}
+
+static void kvm_flush_dynamic_msi_routes(KVMState *s)
+{
+    KVMMSIRoute *route, *next;
+    unsigned int hash;
+
+    for (hash = 0; hash < KVM_MSI_HASHTAB_SIZE; hash++) {
+        QTAILQ_FOREACH_SAFE(route, &s->msi_hashtab[hash], entry, next) {
+            kvm_remove_routing_entry(s, &route->kroute);
+            QTAILQ_REMOVE(&s->msi_hashtab[hash], route, entry);
+            g_free(route);
+        }
+    }
+}
+
+static int kvm_get_pseudo_gsi(KVMState *s)
+{
+    uint32_t *word = s->used_gsi_bitmap;
+    int max_words = ALIGN(s->gsi_count, 32) / 32;
+    int i, bit;
+    bool retry = true;
+
+again:
+    /* Return the lowest unused GSI in the bitmap */
+    for (i = 0; i < max_words; i++) {
+        bit = ffs(~word[i]);
+        if (!bit) {
+            continue;
+        }
+
+        return bit - 1 + i * 32;
+    }
+    if (retry) {
+        retry = false;
+        kvm_flush_dynamic_msi_routes(s);
+        goto again;
+    }
+    return -ENOSPC;
+
+}
+
+static KVMMSIRoute *kvm_lookup_msi_route(KVMState *s, uint64_t addr,
+                                         uint32_t data)
+{
+    unsigned int hash = kvm_hash_msi(data);
+    KVMMSIRoute *route;
+
+    QTAILQ_FOREACH(route, &s->msi_hashtab[hash], entry) {
+        if (route->kroute.u.msi.address_lo == (uint32_t)addr &&
+            route->kroute.u.msi.address_hi == (addr >> 32) &&
+            route->kroute.u.msi.data == data) {
+            return route;
+        }
+    }
+    return NULL;
+}
+
+int kvm_irqchip_send_msi(KVMState *s, uint64_t addr, uint32_t data)
+{
+    KVMMSIRoute *route;
+
+    route = kvm_lookup_msi_route(s, addr, data);
+    if (!route) {
+        int gsi, ret;
+
+        gsi = kvm_get_pseudo_gsi(s);
+        if (gsi < 0) {
+            return gsi;
+        }
+
+        route = g_malloc(sizeof(KVMMSIRoute));
+        route->kroute.gsi = gsi;
+        route->kroute.type = KVM_IRQ_ROUTING_MSI;
+        route->kroute.flags = 0;
+        route->kroute.u.msi.address_lo = (uint32_t)addr;
+        route->kroute.u.msi.address_hi = addr >> 32;
+        route->kroute.u.msi.data = data;
+
+        kvm_add_routing_entry(s, &route->kroute);
+
+        QTAILQ_INSERT_TAIL(&s->msi_hashtab[kvm_hash_msi(data)], route, entry);
+
+        ret = kvm_irqchip_commit_routes(s);
+        if (ret < 0) {
+            return ret;
+        }
+    }
+
+    assert(route->kroute.type == KVM_IRQ_ROUTING_MSI);
+
+    return kvm_irqchip_set_irq(s, route->kroute.gsi, 1);
+}
+
 #else /* !KVM_CAP_IRQ_ROUTING */
 
 static void kvm_init_irq_routing(KVMState *s)
 {
 }
+
+int kvm_irqchip_send_msi(KVMState *s, uint64_t addr, uint32_t data)
+{
+    abort();
+}
 #endif /* !KVM_CAP_IRQ_ROUTING */
 
 static int kvm_irqchip_create(KVMState *s)
diff --git a/kvm.h b/kvm.h
index 4ccae8c..34db583 100644
--- a/kvm.h
+++ b/kvm.h
@@ -132,6 +132,7 @@ int kvm_arch_on_sigbus(int code, void *addr);
 void kvm_arch_init_irq_routing(KVMState *s);
 
 int kvm_irqchip_set_irq(KVMState *s, int irq, int level);
+int kvm_irqchip_send_msi(KVMState *s, uint64_t addr, uint32_t data);
 
 void kvm_irqchip_add_route(KVMState *s, int gsi, int irqchip, int pin);
 int kvm_irqchip_commit_routes(KVMState *s);
-- 
1.7.3.4




reply via email to

[Prev in Thread] Current Thread [Next in Thread]