]> rtime.felk.cvut.cz Git - jailhouse.git/commitdiff
arm: Disable maintenance interrupt on successful injection
authorJan Kiszka <jan.kiszka@siemens.com>
Tue, 14 Jun 2016 05:30:09 +0000 (07:30 +0200)
committerJan Kiszka <jan.kiszka@siemens.com>
Thu, 16 Jun 2016 16:41:13 +0000 (18:41 +0200)
We enable the maintenance interrupt when all list registers are in use.
However, there was no disabling of it again. Apparently, it rarely
triggered in the field, otherwise we would have seen a lot of
maintenance interrupt storms, thus locked-up systems.

This introduces another callback to enable or disable the maintenance
interrupt. It is now controlled by irqchip_inject_pending, the function
that is also called when handling a maintenance interrupt.

Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
hypervisor/arch/arm/gic-v2.c
hypervisor/arch/arm/gic-v3.c
hypervisor/arch/arm/include/asm/irqchip.h
hypervisor/arch/arm/irqchip.c

index ce5a61c44b4f846a0f9213c6cbec899d3874fd69..e47c1424eb1cc692f43f3ad88bd0d3a12c1ed70a 100644 (file)
@@ -251,15 +251,8 @@ static int gic_inject_irq(struct per_cpu *cpu_data, struct pending_irq *irq)
                        return -EINVAL;
        }
 
-       if (first_free == -1) {
-               /* Enable maintenance IRQ */
-               u32 hcr;
-               hcr = mmio_read32(gich_base + GICH_HCR);
-               hcr |= GICH_HCR_UIE;
-               mmio_write32(gich_base + GICH_HCR, hcr);
-
+       if (first_free == -1)
                return -EBUSY;
-       }
 
        /* Inject group 0 interrupt (seen as IRQ by the guest) */
        lr = irq->virt_id;
@@ -275,6 +268,18 @@ static int gic_inject_irq(struct per_cpu *cpu_data, struct pending_irq *irq)
        return 0;
 }
 
+static void gic_enable_maint_irq(bool enable)
+{
+       u32 hcr;
+
+       hcr = mmio_read32(gich_base + GICH_HCR);
+       if (enable)
+               hcr |= GICH_HCR_UIE;
+       else
+               hcr &= ~GICH_HCR_UIE;
+       mmio_write32(gich_base + GICH_HCR, hcr);
+}
+
 unsigned int irqchip_mmio_count_regions(struct cell *cell)
 {
        return 1;
@@ -290,5 +295,6 @@ struct irqchip_ops gic_irqchip = {
        .send_sgi = gic_send_sgi,
        .handle_irq = gic_handle_irq,
        .inject_irq = gic_inject_irq,
+       .enable_maint_irq = gic_enable_maint_irq,
        .eoi_irq = gic_eoi_irq,
 };
index 6beab34433d0c47fe8f2f28c5b9cb3ce71a1bb42..fe9c261f227a8ba382a4cc2c1ef234ee51ac683f 100644 (file)
@@ -370,18 +370,9 @@ static int gic_inject_irq(struct per_cpu *cpu_data, struct pending_irq *irq)
                        return -EINVAL;
        }
 
-       if (free_lr == -1) {
-               u32 hcr;
-               /*
-                * All list registers are in use, trigger a maintenance
-                * interrupt once they are available again.
-                */
-               arm_read_sysreg(ICH_HCR_EL2, hcr);
-               hcr |= ICH_HCR_UIE;
-               arm_write_sysreg(ICH_HCR_EL2, hcr);
-
+       if (free_lr == -1)
+               /* All list registers are in use */
                return -EBUSY;
-       }
 
        lr = irq->virt_id;
        /* Only group 1 interrupts */
@@ -397,6 +388,18 @@ static int gic_inject_irq(struct per_cpu *cpu_data, struct pending_irq *irq)
        return 0;
 }
 
+static void gicv3_enable_maint_irq(bool enable)
+{
+       u32 hcr;
+
+       arm_read_sysreg(ICH_HCR_EL2, hcr);
+       if (enable)
+               hcr |= ICH_HCR_UIE;
+       else
+               hcr &= ~ICH_HCR_UIE;
+       arm_write_sysreg(ICH_HCR_EL2, hcr);
+}
+
 unsigned int irqchip_mmio_count_regions(struct cell *cell)
 {
        return 2;
@@ -411,5 +414,6 @@ struct irqchip_ops gic_irqchip = {
        .send_sgi = gic_send_sgi,
        .handle_irq = gic_handle_irq,
        .inject_irq = gic_inject_irq,
+       .enable_maint_irq = gicv3_enable_maint_irq,
        .eoi_irq = gic_eoi_irq,
 };
index c4b8289f19cf581468e64db3d7d065bacb3c05ce..538b432234e03b514426536a1b4be18b86c04622 100644 (file)
@@ -56,6 +56,7 @@ struct irqchip_ops {
        void    (*eoi_irq)(u32 irqn, bool deactivate);
        int     (*inject_irq)(struct per_cpu *cpu_data,
                              struct pending_irq *irq);
+       void    (*enable_maint_irq)(bool enable);
 
        int     (*mmio_access)(struct mmio_access *access);
 };
index 7ace3544ef3aef4e2f9aa0e03a3aaa50246526ac..518739000065a56ed4a43f813dd2f7b63e688fd3 100644 (file)
@@ -167,10 +167,14 @@ void irqchip_inject_pending(struct per_cpu *cpu_data)
 
        while (pending != NULL) {
                err = irqchip.inject_irq(cpu_data, pending);
-               if (err == -EBUSY)
-                       /* The list registers are full. */
-                       break;
-               else
+               if (err == -EBUSY) {
+                       /*
+                        * The list registers are full, trigger maintenance
+                        * interrupt and leave.
+                        */
+                       irqchip.enable_maint_irq(true);
+                       return;
+               } else {
                        /*
                         * Removal only changes the pointers, but does not
                         * deallocate anything.
@@ -180,9 +184,16 @@ void irqchip_inject_pending(struct per_cpu *cpu_data)
                         * after this removal, which isn't an issue.
                         */
                        irqchip_remove_pending(cpu_data, pending);
+               }
 
                pending = pending->next;
        }
+
+       /*
+        * The software interrupt queue is empty - turn off the maintenance
+        * interrupt.
+        */
+       irqchip.enable_maint_irq(false);
 }
 
 void irqchip_handle_irq(struct per_cpu *cpu_data)