Use gsi indexed array instead of scanning all entries on each interrupt
injection. Also maintain back mapping from irqchip/pin to gsi to speedup
interrupt acknowledgment notifications.

Signed-off-by: Gleb Natapov <g...@redhat.com>
---
 include/linux/kvm_host.h |   11 +++++++-
 virt/kvm/irq_comm.c      |   57 +++++++++++++++++++++++++++++----------------
 2 files changed, 46 insertions(+), 22 deletions(-)

diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 6bcc79f..2715e59 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -128,7 +128,14 @@ struct kvm_kernel_irq_routing_entry {
                } irqchip;
                struct msi_msg msi;
        };
-       struct list_head link;
+       struct hlist_node link;
+};
+
+struct kvm_irq_routing_table {
+       int chip[3][KVM_IOAPIC_NUM_PINS];
+       struct kvm_kernel_irq_routing_entry *rt_entries;
+       u32 max_gsi;
+       struct hlist_head map[0];
 };
 
 struct kvm {
@@ -165,7 +172,7 @@ struct kvm {
 #endif
 
 #ifdef CONFIG_HAVE_KVM_IRQCHIP
-       struct kvm_kernel_irq_routing_entry *irq_routing;
+       struct kvm_irq_routing_table *irq_routing;
        spinlock_t irq_routing_lock;
        struct hlist_head mask_notifier_list;
        struct hlist_head irq_ack_notifier_list;
diff --git a/virt/kvm/irq_comm.c b/virt/kvm/irq_comm.c
index be30124..ae11114 100644
--- a/virt/kvm/irq_comm.c
+++ b/virt/kvm/irq_comm.c
@@ -128,6 +128,8 @@ static int __kvm_set_irq(struct kvm *kvm, int 
irq_source_id, int irq, int level,
        struct kvm_kernel_irq_routing_entry *e;
        unsigned long *irq_state, sig_level;
        int ret = -1;
+       struct kvm_irq_routing_table *irq_rt;
+       struct hlist_node *n;
 
        trace_kvm_set_irq(irq, level, irq_source_id);
 
@@ -150,15 +152,15 @@ static int __kvm_set_irq(struct kvm *kvm, int 
irq_source_id, int irq, int level,
         * writes to the unused one.
         */
        rcu_read_lock();
-       for (e = rcu_dereference(kvm->irq_routing); e && e->set; e++) {
-               if (e->gsi == irq) {
+       irq_rt = rcu_dereference(kvm->irq_routing);
+       if (irq < irq_rt->max_gsi)
+               hlist_for_each_entry(e, n, &irq_rt->map[irq], link) {
                        int r = e->set(e, kvm, sig_level, notifier);
                        if (r < 0)
                                continue;
 
                        ret = r + ((ret < 0) ? 0 : ret);
                }
-       }
        rcu_read_unlock();
        return ret;
 }
@@ -175,21 +177,16 @@ int kvm_notifier_set_irq(struct kvm *kvm, int 
irq_source_id, int irq, int level)
 
 void kvm_notify_acked_irq(struct kvm *kvm, unsigned irqchip, unsigned pin)
 {
-       struct kvm_kernel_irq_routing_entry *e;
        struct kvm_irq_ack_notifier *kian;
        struct hlist_node *n;
-       unsigned gsi = pin;
+       unsigned gsi;
 
        trace_kvm_ack_irq(irqchip, pin);
 
        rcu_read_lock();
-       for (e = rcu_dereference(kvm->irq_routing); e && e->set; e++) {
-               if (e->irqchip.irqchip == irqchip &&
-                   e->irqchip.pin == pin) {
-                       gsi = e->gsi;
-                       break;
-               }
-       }
+       gsi = rcu_dereference(kvm->irq_routing)->chip[irqchip][pin];
+       if (gsi == -1)
+               gsi = pin;
 
        hlist_for_each_entry_rcu(kian, n, &kvm->irq_ack_notifier_list, link)
                if (kian->gsi == gsi)
@@ -290,7 +287,8 @@ void kvm_free_irq_routing(struct kvm *kvm)
        kfree(kvm->irq_routing);
 }
 
-static int setup_routing_entry(struct kvm_kernel_irq_routing_entry *e,
+static int setup_routing_entry(struct kvm_irq_routing_table *rt,
+                              struct kvm_kernel_irq_routing_entry *e,
                               const struct kvm_irq_routing_entry *ue)
 {
        int r = -EINVAL;
@@ -316,6 +314,9 @@ static int setup_routing_entry(struct 
kvm_kernel_irq_routing_entry *e,
                }
                e->irqchip.irqchip = ue->u.irqchip.irqchip;
                e->irqchip.pin = ue->u.irqchip.pin + delta;
+               if (e->irqchip.pin > KVM_IOAPIC_NUM_PINS)
+                       goto out;
+               rt->chip[ue->u.irqchip.irqchip][e->irqchip.pin] = ue->gsi;
                break;
        case KVM_IRQ_ROUTING_MSI:
                e->set = kvm_set_msi;
@@ -326,6 +327,8 @@ static int setup_routing_entry(struct 
kvm_kernel_irq_routing_entry *e,
        default:
                goto out;
        }
+
+       hlist_add_head(&e->link, &rt->map[e->gsi]);
        r = 0;
 out:
        return r;
@@ -337,23 +340,37 @@ int kvm_set_irq_routing(struct kvm *kvm,
                        unsigned nr,
                        unsigned flags)
 {
-       struct kvm_kernel_irq_routing_entry *new, *old;
-       unsigned i;
+       struct kvm_irq_routing_table *new, *old;
+       u32 i, j, max_gsi = 0;
        int r;
 
-       /* last elemet is left zeored and indicates the end of the array */
-       new = kzalloc(sizeof(*new) * (nr + 1), GFP_KERNEL);
+       for (i = 0; i < nr; ++i) {
+               if (ue[i].gsi >= KVM_MAX_IRQ_ROUTES)
+                       return -EINVAL;
+               max_gsi = max(max_gsi, ue[i].gsi);
+       }
+
+       max_gsi += 1;
+
+       new = kzalloc(sizeof(*new) + (max_gsi * sizeof(struct hlist_head)) +
+                     (nr * sizeof(struct kvm_kernel_irq_routing_entry)),
+                     GFP_KERNEL);
 
        if (!new)
                return -ENOMEM;
 
+       new->rt_entries = (void *)&new->map[max_gsi];
+
+       new->max_gsi = max_gsi;
+       for (i = 0; i < 3; i++)
+               for (j = 0; j < KVM_IOAPIC_NUM_PINS; j++)
+                       new->chip[i][j] = -1;
+
        for (i = 0; i < nr; ++i) {
                r = -EINVAL;
-               if (ue->gsi >= KVM_MAX_IRQ_ROUTES)
-                       goto out;
                if (ue->flags)
                        goto out;
-               r = setup_routing_entry(new + i, ue);
+               r = setup_routing_entry(new, &new->rt_entries[i], ue);
                if (r)
                        goto out;
                ++ue;
-- 
1.6.2.1

--
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