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>
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;
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;
.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,
};
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 */
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;
.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,
};
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);
};
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.
* 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)