Re: [PATCH 06/16] ARM: KVM: vgic: refactor level irq handling

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

 





On Thursday, December 6, 2012, Marc Zyngier wrote:
Shuffle things around to make handling of level interrupts less
messy:
- encapsulate irq_active in accessors
- introduce vgic_queue_{sgi,hwirq} functions
- vgic_is_edge now takes a vcpu instrad of a distributor

instead
 

Signed-off-by: Marc Zyngier <marc.zyngier@xxxxxxx>
---
 arch/arm/kvm/vgic.c | 172 ++++++++++++++++++++++++++++++----------------------
 1 file changed, 100 insertions(+), 72 deletions(-)

diff --git a/arch/arm/kvm/vgic.c b/arch/arm/kvm/vgic.c
index 11a4416..d666df7 100644
--- a/arch/arm/kvm/vgic.c
+++ b/arch/arm/kvm/vgic.c
@@ -154,9 +154,11 @@ static u32 *vgic_bytemap_get_reg(struct vgic_bytemap *x, int cpuid, u32 offset)
 #define VGIC_CFG_LEVEL 0
 #define VGIC_CFG_EDGE  1

-static bool vgic_irq_is_edge(struct vgic_dist *dist, int irq)
+static bool vgic_irq_is_edge(struct kvm_vcpu *vcpu, int irq)
 {
-       return vgic_bitmap_get_irq_val(&dist->irq_cfg, 0, irq) == VGIC_CFG_EDGE;
+       struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+
+       return vgic_bitmap_get_irq_val(&dist->irq_cfg, vcpu->vcpu_id, irq) == VGIC_CFG_EDGE;
 }

 static int vgic_irq_is_enabled(struct kvm_vcpu *vcpu, int irq)
@@ -166,6 +168,31 @@ static int vgic_irq_is_enabled(struct kvm_vcpu *vcpu, int irq)
        return vgic_bitmap_get_irq_val(&dist->irq_enabled, vcpu->vcpu_id, irq);
 }

+static int vgic_irq_is_active(struct kvm_vcpu *vcpu, int irq)
+{
+       struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+
+       return vgic_bitmap_get_irq_val(&dist->irq_active, vcpu->vcpu_id, irq);
+}
+
+static void vgic_irq_set_active(struct kvm_vcpu *vcpu, int irq)
+{
+       struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+
+       vgic_bitmap_set_irq_val(&dist->irq_active, vcpu->vcpu_id, irq, 1);
+       atomic_inc(&vcpu->arch.vgic_cpu.irq_active_count);
+       smp_mb__after_atomic_inc();
+}
+
+static void vgic_irq_clear_active(struct kvm_vcpu *vcpu, int irq)
+{
+       struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+
+       vgic_bitmap_set_irq_val(&dist->irq_active, vcpu->vcpu_id, irq, 0);
+       atomic_dec(&vcpu->arch.vgic_cpu.irq_active_count);
+       smp_mb__after_atomic_dec();

uf, could we have a comment explaining these memory barriers?

Specifically, what is the change after the decrement, which must be perceived after the decrement from another cpu?
 
+}
+
 static void vgic_dist_irq_set(struct kvm_vcpu *vcpu, int irq)
 {
        struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
@@ -809,8 +836,7 @@ static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu)
 static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq)
 {
        struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
-       struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
-       int lr, is_level;
+       int lr;

        /* Sanitize the input... */
        BUG_ON(sgi_source_id & ~7);
@@ -820,7 +846,6 @@ static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq)
        kvm_debug("Queue IRQ%d\n", irq);

        lr = vgic_cpu->vgic_irq_lr_map[irq];
-       is_level = !vgic_irq_is_edge(dist, irq);

        /* Do we have an active interrupt for the same CPUID? */
        if (lr != LR_EMPTY &&
@@ -828,11 +853,8 @@ static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq)
                kvm_debug("LR%d piggyback for IRQ%d %x\n", lr, irq, vgic_cpu->vgic_lr[lr]);
                BUG_ON(!test_bit(lr, vgic_cpu->lr_used));
                vgic_cpu->vgic_lr[lr] |= VGIC_LR_PENDING_BIT;
-               if (is_level) {
-                       vgic_cpu->vgic_lr[lr] |= VGIC_LR_EOI;
-                       atomic_inc(&vgic_cpu->irq_active_count);
-               }
-               return true;


why is this no longer necessary?
 
+
+               goto out;
        }

        /* Try to use another LR for this interrupt */
@@ -843,17 +865,68 @@ static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq)

        kvm_debug("LR%d allocated for IRQ%d %x\n", lr, irq, sgi_source_id);
        vgic_cpu->vgic_lr[lr] = MK_LR_PEND(sgi_source_id, irq);
-       if (is_level) {
-               vgic_cpu->vgic_lr[lr] |= VGIC_LR_EOI;
-               atomic_inc(&vgic_cpu->irq_active_count);
-       }
-

and this, why can it just be removed?
 
        vgic_cpu->vgic_irq_lr_map[irq] = lr;
        set_bit(lr, vgic_cpu->lr_used);

+out:
+       if (!vgic_irq_is_edge(vcpu, irq))
+               vgic_cpu->vgic_lr[lr] |= VGIC_LR_EOI;
+
        return true;
 }

+static bool vgic_queue_sgi(struct kvm_vcpu *vcpu, int irq)
+{
+       struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+       unsigned long sources;
+       int vcpu_id = vcpu->vcpu_id;
+       int c;
+
+       sources = dist->irq_sgi_sources[vcpu_id][irq];
+
+       for_each_set_bit(c, &sources, VGIC_MAX_CPUS) {
+               if (vgic_queue_irq(vcpu, c, irq))
+                       clear_bit(c, &sources);
+       }
+
+       dist->irq_sgi_sources[vcpu_id][irq] = sources;
+
+       /*
+        * If the sources bitmap has been cleared it means that we
+        * could queue all the SGIs onto link registers (see the
+        * clear_bit above), and therefore we are done with them in
+        * our emulated gic and can get rid of them.
+        */
+       if (!sources) {
+               vgic_dist_irq_clear(vcpu, irq);
+               vgic_cpu_irq_clear(vcpu, irq);
+               return true;
+       }
+
+       return false;
+}
+
+static bool vgic_queue_hwirq(struct kvm_vcpu *vcpu, int irq)
+{
+       struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+
+       if (vgic_bitmap_get_irq_val(&dist->irq_active, vcpu->vcpu_id, irq))
+               return true; /* level interrupt, already queued */
+
+       if (vgic_queue_irq(vcpu, 0, irq)) {
+               if (vgic_irq_is_edge(vcpu, irq)) {
+                       vgic_dist_irq_clear(vcpu, irq);
+                       vgic_cpu_irq_clear(vcpu, irq);
+               } else {
+                       vgic_irq_set_active(vcpu, irq);
+               }
+
+               return true;
+       }
+
+       return false;
+}
+
 /*
  * Fill the list registers with pending interrupts before running the
  * guest.
@@ -862,7 +935,7 @@ static void __kvm_vgic_sync_to_cpu(struct kvm_vcpu *vcpu)
 {
        struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
        struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
-       int i, c, vcpu_id;
+       int i, vcpu_id;
        int overflow = 0;

        vcpu_id = vcpu->vcpu_id;
@@ -881,62 +954,20 @@ static void __kvm_vgic_sync_to_cpu(struct kvm_vcpu *vcpu)

        /* SGIs */
        for_each_set_bit(i, vgic_cpu->pending_percpu, VGIC_NR_SGIS) {
-               unsigned long sources;
-
-               sources = dist->irq_sgi_sources[vcpu_id][i];
-               for_each_set_bit(c, &sources, 8) {
-                       if (!vgic_queue_irq(vcpu, c, i)) {
-                               overflow = 1;
-                               continue;
-                       }
-
-                       clear_bit(c, &sources);
-               }
-
-               /*
-                * If the sources bitmap has been cleared it means that we
-                * could queue all the SGIs onto link registers (see the
-                * clear_bit above), and therefore we are done with them in
-                * our emulated gic and can get rid of them.
-                */
-               if (!sources) {
-                       vgic_dist_irq_clear(vcpu, i);
-                       vgic_cpu_irq_clear(vcpu, i);
-               }
-
-               dist->irq_sgi_sources[vcpu_id][i] = sources;
+               if (!vgic_queue_sgi(vcpu, i))
+                       overflow = 1;
        }

        /* PPIs */
        for_each_set_bit_from(i, vgic_cpu->pending_percpu, VGIC_NR_PRIVATE_IRQS) {
-               if (!vgic_queue_irq(vcpu, 0, i)) {
+               if (!vgic_queue_hwirq(vcpu, i))
                        overflow = 1;
-                       continue;
-               }
-
-               vgic_dist_irq_clear(vcpu, i);
-               vgic_cpu_irq_clear(vcpu, i);
        }

-
        /* SPIs */
        for_each_set_bit(i, vgic_cpu->pending_shared, VGIC_NR_SHARED_IRQS) {
-               int irq = i + VGIC_NR_PRIVATE_IRQS;
-               if (vgic_bitmap_get_irq_val(&dist->irq_active, 0, irq))
-                       continue; /* level interrupt, already queued */
-
-               if (!vgic_queue_irq(vcpu, 0, irq)) {
+               if (!vgic_queue_hwirq(vcpu, i + VGIC_NR_PRIVATE_IRQS))
                        overflow = 1;
-                       continue;
-               }
-
-               /* Immediate clear on edge, set active on level */
-               if (vgic_irq_is_edge(dist, irq)) {
-                       vgic_dist_irq_clear(vcpu, irq);
-                       vgic_cpu_irq_clear(vcpu, irq);
-               } else {
-                       vgic_bitmap_set_irq_val(&dist->irq_active, 0, irq, 1);
-               }
        }

 epilog:
@@ -1044,7 +1075,8 @@ static bool vgic_update_irq_state(struct kvm *kvm, int cpuid,

        spin_lock(&dist->lock);

-       is_edge = vgic_irq_is_edge(dist, irq_num);
+       vcpu = kvm_get_vcpu(kvm, cpuid);
+       is_edge = vgic_irq_is_edge(vcpu, irq_num);
        is_level = !is_edge;
        state = vgic_bitmap_get_irq_val(&dist->irq_state, cpuid, irq_num);

@@ -1058,13 +1090,13 @@ static bool vgic_update_irq_state(struct kvm *kvm, int cpuid,
                goto out;
        }

-       if (irq_num >= VGIC_NR_PRIVATE_IRQS)
+       if (irq_num >= VGIC_NR_PRIVATE_IRQS) {
                cpuid = dist->irq_spi_cpu[irq_num - VGIC_NR_PRIVATE_IRQS];
+               vcpu = kvm_get_vcpu(kvm, cpuid);
+       }

        kvm_debug("Inject IRQ%d level %d CPU%d\n", irq_num, level, cpuid);

-       vcpu = kvm_get_vcpu(kvm, cpuid);
-
        if (level)
                vgic_dist_irq_set(vcpu, irq_num);
        else
@@ -1077,8 +1109,7 @@ static bool vgic_update_irq_state(struct kvm *kvm, int cpuid,
                goto out;
        }

-       if (is_level && vgic_bitmap_get_irq_val(&dist->irq_active,
-                                               cpuid, irq_num)) {
+       if (is_level && vgic_irq_is_active(vcpu, irq_num)) {
                /*
                 * Level interrupt in progress, will be picked up
                 * when EOId.
@@ -1159,10 +1190,7 @@ static irqreturn_t vgic_maintenance_handler(int irq, void *data)
                                 vgic_cpu->nr_lr) {
                        irq = vgic_cpu->vgic_lr[lr] & VGIC_LR_VIRTUALID;

-                       vgic_bitmap_set_irq_val(&dist->irq_active,
-                                               vcpu->vcpu_id, irq, 0);
-                       atomic_dec(&vgic_cpu->irq_active_count);
-                       smp_mb();
+                       vgic_irq_clear_active(vcpu, irq);
                        vgic_cpu->vgic_lr[lr] &= ~VGIC_LR_EOI;
                        writel_relaxed(vgic_cpu->vgic_lr[lr],
                                       dist->vctrl_base + GICH_LR0 + (lr << 2));
--
1.8.0.1



_______________________________________________
kvmarm mailing list
kvmarm@xxxxxxxxxxxxxxxxxxxxx
https://lists.cs.columbia.edu/cucslists/listinfo/kvmarm
_______________________________________________
kvmarm mailing list
kvmarm@xxxxxxxxxxxxxxxxxxxxx
https://lists.cs.columbia.edu/cucslists/listinfo/kvmarm

[Index of Archives]     [Linux KVM]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux