@@ -148,6 +148,36 @@ static inline bool primary_gic_irq(struct irq_data *d)
return true;
}
+static inline bool cascading_gic_irq(struct irq_data *d)
+{
+ void *data = irq_data_get_irq_handler_data(d);
+
+ /*
+ * If handler_data pointing to one of the secondary GICs, then
+ * this is a cascading interrupt, and it cannot possibly be
+ * forwarded.
+ */
+ if (data >= (void *)(gic_data + 1) &&
+ data < (void *)(gic_data + MAX_GIC_NR))
+ return true;
+
+ return false;
+}
+
+static inline bool forwarded_irq(struct irq_data *d)
+{
+ /*
+ * A forwarded interrupt:
+ * - is on the primary GIC
+ * - has its handler_data set to a value
+ * - that isn't a secondary GIC
+ */
+ if (primary_gic_irq(d) && d->handler_data && !cascading_gic_irq(d))
+ return true;
+
+ return false;
+}
+
/*
* Routines to acknowledge, disable and enable interrupts
*/
@@ -166,6 +196,18 @@ static int gic_peek_irq(struct irq_data *d, u32 offset)
static void gic_mask_irq(struct irq_data *d)
{
gic_poke_irq(d, GIC_DIST_ENABLE_CLEAR);
+ /*
+ * When masking a forwarded interrupt, make sure it is
+ * deactivated as well.
+ *
+ * This ensures that an interrupt that is getting
+ * disabled/masked will not get "stuck", because there is
+ * noone to deactivate it (guest is being terminated).
+ */
+ if (static_key_true(&supports_deactivate)) {
+ if (forwarded_irq(d))
+ gic_poke_irq(d, GIC_DIST_ACTIVE_CLEAR);
+ }
}
static void gic_unmask_irq(struct irq_data *d)
@@ -178,6 +220,10 @@ static void gic_eoi_irq(struct irq_data *d)
u32 deact_offset = GIC_CPU_EOI;
if (static_key_true(&supports_deactivate)) {
+ /* Do not deactivate an IRQ forwarded to a vcpu. */
+ if (forwarded_irq(d))
+ return;
+
if (primary_gic_irq(d))
deact_offset = GIC_CPU_DEACTIVATE;
}
@@ -251,6 +297,19 @@ static int gic_set_type(struct irq_data *d, unsigned int type)
return gic_configure_irq(gicirq, type, base, NULL);
}
+static int gic_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu)
+{
+ /* Only interrupts on the primary GIC can be forwarded to a vcpu. */
+ if (static_key_true(&supports_deactivate)) {
+ if (primary_gic_irq(d) && !cascading_gic_irq(d)) {
+ d->handler_data = vcpu;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
#ifdef CONFIG_SMP
static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
bool force)
@@ -346,6 +405,7 @@ static struct irq_chip gic_chip = {
#endif
.irq_get_irqchip_state = gic_irq_get_irqchip_state,
.irq_set_irqchip_state = gic_irq_set_irqchip_state,
+ .irq_set_vcpu_affinity = gic_irq_set_vcpu_affinity,
.flags = IRQCHIP_SET_TYPE_MASKED,
};