From 35b1928d4977040a4a9f4fc1f2a093882182a953 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 14 Jun 2016 07:30:09 +0200 Subject: [PATCH] arm: Disable maintenance interrupt on successful injection 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 --- hypervisor/arch/arm/gic-v2.c | 22 ++++++++++++------- hypervisor/arch/arm/gic-v3.c | 26 +++++++++++++---------- hypervisor/arch/arm/include/asm/irqchip.h | 1 + hypervisor/arch/arm/irqchip.c | 19 +++++++++++++---- 4 files changed, 45 insertions(+), 23 deletions(-) diff --git a/hypervisor/arch/arm/gic-v2.c b/hypervisor/arch/arm/gic-v2.c index ce5a61c..e47c142 100644 --- a/hypervisor/arch/arm/gic-v2.c +++ b/hypervisor/arch/arm/gic-v2.c @@ -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, }; diff --git a/hypervisor/arch/arm/gic-v3.c b/hypervisor/arch/arm/gic-v3.c index 6beab34..fe9c261 100644 --- a/hypervisor/arch/arm/gic-v3.c +++ b/hypervisor/arch/arm/gic-v3.c @@ -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, }; diff --git a/hypervisor/arch/arm/include/asm/irqchip.h b/hypervisor/arch/arm/include/asm/irqchip.h index c4b8289..538b432 100644 --- a/hypervisor/arch/arm/include/asm/irqchip.h +++ b/hypervisor/arch/arm/include/asm/irqchip.h @@ -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); }; diff --git a/hypervisor/arch/arm/irqchip.c b/hypervisor/arch/arm/irqchip.c index 7ace354..5187390 100644 --- a/hypervisor/arch/arm/irqchip.c +++ b/hypervisor/arch/arm/irqchip.c @@ -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) -- 2.39.2